diff --git a/test/p2p/iatfTest/.gitignore b/test/p2p/iatfTest/.gitignore
new file mode 100644
index 00000000..796b96d1
--- /dev/null
+++ b/test/p2p/iatfTest/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/test/p2p/iatfTest/build.gradle b/test/p2p/iatfTest/build.gradle
new file mode 100644
index 00000000..3ae79497
--- /dev/null
+++ b/test/p2p/iatfTest/build.gradle
@@ -0,0 +1,41 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 21
+ buildToolsVersion rootProject.ext.buildToolsVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.minSdkVersion
+ targetSdkVersion 21
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+
+repositories {
+ flatDir {
+ dirs '../../libs'
+ }
+}
+
+dependencies {
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test:rules:1.0.2'
+ implementation project(':test:p2p:util')
+}
diff --git a/test/p2p/iatfTest/proguard-rules.pro b/test/p2p/iatfTest/proguard-rules.pro
new file mode 100644
index 00000000..f1b42451
--- /dev/null
+++ b/test/p2p/iatfTest/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/test/p2p/iatfTest/src/androidTest/java/owt/test/p2p/apitest/TestDevice1.java b/test/p2p/iatfTest/src/androidTest/java/owt/test/p2p/apitest/TestDevice1.java
new file mode 100644
index 00000000..4a904175
--- /dev/null
+++ b/test/p2p/iatfTest/src/androidTest/java/owt/test/p2p/apitest/TestDevice1.java
@@ -0,0 +1,67 @@
+package owt.test.p2p.apitest;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import owt.p2p.P2PClient;
+import owt.test.util.LockServer;
+
+
+@RunWith(AndroidJUnit4.class)
+public class TestDevice1 {
+ private static LockServer mLockServer = new LockServer();
+ private static String taskId = null;
+ private int caseSequenceNumber = 0;
+
+ @Test
+ public void testConnect() {
+ mLockServer.notifyWorkflowLock("user1", "connect");
+ mLockServer.waitWorkflowLock("user2", "connect");
+ }
+
+ @BeforeClass
+ public static void beforeClass() {
+ taskId = InstrumentationRegistry.getArguments().getString("taskId");
+ mLockServer.connect(taskId,"role1");
+ mLockServer.waitControlLock("task-start");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ mLockServer.disconnect();
+ }
+
+ @After
+ public void tearDown() {
+ JSONObject lockData = new JSONObject();
+ try {
+ lockData.put("sequenceNumber", this.caseSequenceNumber);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ mLockServer.notifyControlLock("case-end", lockData);
+ mLockServer.initStatus();
+ }
+
+ @Before
+ public void setUp() {
+ this.caseSequenceNumber++;
+ JSONObject lockData = new JSONObject();
+ try {
+ lockData.put("sequenceNumber", this.caseSequenceNumber);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ mLockServer.notifyControlLock("case-ready", lockData);
+ mLockServer.waitControlLock("case-start");
+ }
+}
diff --git a/test/p2p/iatfTest/src/androidTest/java/owt/test/p2p/apitest/TestDevice2.java b/test/p2p/iatfTest/src/androidTest/java/owt/test/p2p/apitest/TestDevice2.java
new file mode 100644
index 00000000..3bd32c91
--- /dev/null
+++ b/test/p2p/iatfTest/src/androidTest/java/owt/test/p2p/apitest/TestDevice2.java
@@ -0,0 +1,67 @@
+package owt.test.p2p.apitest;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import owt.test.util.LockServer;
+
+@RunWith(AndroidJUnit4.class)
+public class TestDevice2 {
+ private static LockServer mLockServer = new LockServer();
+ private static String taskId = null;
+ private int caseSequenceNumber = 0;
+
+ @Test
+ public void testConnect() {
+ mLockServer.waitWorkflowLock("user1", "connect");
+ mLockServer.notifyWorkflowLock("user2", "connect");
+ }
+
+ @BeforeClass
+ public static void beforeClass() {
+ taskId = InstrumentationRegistry.getArguments().getString("taskId");
+ mLockServer.connect(taskId,"role2");
+ mLockServer.waitControlLock("task-start");
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ mLockServer.disconnect();
+
+ }
+
+ @After
+ public void tearDown() {
+ JSONObject lockData = new JSONObject();
+ try {
+ lockData.put("sequenceNumber", this.caseSequenceNumber);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ mLockServer.notifyControlLock("case-end", lockData);
+ mLockServer.initStatus();
+ }
+
+ @Before
+ public void setUp() {
+ this.caseSequenceNumber++;
+ JSONObject lockData = new JSONObject();
+ try {
+ lockData.put("sequenceNumber", caseSequenceNumber);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ mLockServer.notifyControlLock("case-ready", lockData);
+ mLockServer.waitControlLock("case-start");
+ }
+
+}
diff --git a/test/p2p/iatfTest/src/main/AndroidManifest.xml b/test/p2p/iatfTest/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..6ca5e765
--- /dev/null
+++ b/test/p2p/iatfTest/src/main/AndroidManifest.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/p2p/iatfTest/src/main/java/owt/test/p2p/apitest/MainActivity.java b/test/p2p/iatfTest/src/main/java/owt/test/p2p/apitest/MainActivity.java
new file mode 100644
index 00000000..9438f38d
--- /dev/null
+++ b/test/p2p/iatfTest/src/main/java/owt/test/p2p/apitest/MainActivity.java
@@ -0,0 +1,16 @@
+package owt.test.p2p.apitest;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+
+public class MainActivity extends Activity {
+ private final static String TAG = "owt_p2p_iatf_test";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_test);
+ }
+
+}
diff --git a/test/p2p/iatfTest/src/main/res/drawable-xhdpi/ic_launcher.png b/test/p2p/iatfTest/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..71c6d760
Binary files /dev/null and b/test/p2p/iatfTest/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/test/p2p/iatfTest/src/main/res/layout/activity_test.xml b/test/p2p/iatfTest/src/main/res/layout/activity_test.xml
new file mode 100644
index 00000000..b9550356
--- /dev/null
+++ b/test/p2p/iatfTest/src/main/res/layout/activity_test.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/p2p/iatfTest/src/main/res/values/strings.xml b/test/p2p/iatfTest/src/main/res/values/strings.xml
new file mode 100644
index 00000000..8ffa8afc
--- /dev/null
+++ b/test/p2p/iatfTest/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ owt_p2p_iatf_test
+ owt_p2p_iatf_test
+
diff --git a/test/util/build.gradle b/test/util/build.gradle
index 8dfc1c91..d4e2f960 100644
--- a/test/util/build.gradle
+++ b/test/util/build.gradle
@@ -26,6 +26,10 @@ repositories {
}
}
dependencies {
+ implementation('io.socket:socket.io-client:1.0.0') {
+ // excluding org.json which is provided by Android
+ exclude group: 'org.json', module: 'json'
+ }
implementation 'org.dom4j:dom4j:2.0.2'
api project(':src:sdk:base')
api project(':src:sdk:conference')
diff --git a/test/util/src/main/java/owt/test/util/Config.java b/test/util/src/main/java/owt/test/util/Config.java
index aa5be987..8f41b39a 100644
--- a/test/util/src/main/java/owt/test/util/Config.java
+++ b/test/util/src/main/java/owt/test/util/Config.java
@@ -53,6 +53,10 @@ public class Config {
public static final String RAW_STREAM_FILE;
+ public static final String IATF_SERVER;
+
+ public static final String IATF_WAIT_TIME;
+
static {
InputStream config = Thread.currentThread().getContextClassLoader().getResourceAsStream(
"config.xml");
@@ -82,6 +86,14 @@ public class Config {
Element rawStreamFileElement = rootElement.element("rawStreamFile");
RAW_STREAM_FILE = rawStreamFileElement == null ? "" : rawStreamFileElement.getText();
+
+ Element iatfServerElement = rootElement.element("iatfServer");
+ IATF_SERVER = iatfServerElement == null ? "" : iatfServerElement.getText();
+
+
+ Element iatfWaitLockTimeElement = rootElement.element("iatfWaitLockTime");
+ IATF_WAIT_TIME = iatfWaitLockTimeElement == null ? "" : iatfWaitLockTimeElement.getText();
+
} catch (DocumentException e) {
throw new RuntimeException(e.getMessage());
}
diff --git a/test/util/src/main/java/owt/test/util/LockServer.java b/test/util/src/main/java/owt/test/util/LockServer.java
new file mode 100644
index 00000000..ea4d400d
--- /dev/null
+++ b/test/util/src/main/java/owt/test/util/LockServer.java
@@ -0,0 +1,215 @@
+package owt.test.util;
+
+import static junit.framework.Assert.assertTrue;
+
+import static owt.test.util.Config.IATF_SERVER;
+import static owt.test.util.Config.IATF_WAIT_TIME;
+
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.net.URISyntaxException;
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+
+import io.socket.client.IO;
+import io.socket.client.Socket;
+import io.socket.emitter.Emitter;
+import okhttp3.OkHttpClient;
+
+
+public class LockServer {
+ private final static String TAG = "iatf_test_util";
+ public String controlMsg = "";
+ public String targetcontrolMsg = "";
+ public String targetworkFlowMsgType = "";
+ public String targetworkFlowMsgData = "";
+ public String workFlowMsgType = "";
+ public String workFlowMsgData = "";
+ private Socket mSocket = null;
+ private final ExecutorService callbackExecutor = Executors.newSingleThreadExecutor();
+ static SSLContext sslContext;
+ static HostnameVerifier hostnameVerifier;
+ private CountDownLatch latch = new CountDownLatch(1);
+ public Object lockControl = new Object();
+ public Object lockWorkFlow = new Object();
+
+ public void initStatus(){
+ this.controlMsg = "";
+ this.targetcontrolMsg = "";
+ this.targetworkFlowMsgType = "";
+ this.targetworkFlowMsgData = "";
+ this.workFlowMsgData = "";
+ this.workFlowMsgType = "";
+ }
+
+ public static void setUpINSECURESSLContext() {
+ hostnameVerifier = (hostname, session) -> true;
+
+ TrustManager[] trustManagers = new TrustManager[]{new X509TrustManager() {
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws
+ CertificateException {
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws
+ CertificateException {
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ return new X509Certificate[0];
+ }
+ }};
+
+ try {
+ sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, trustManagers, null);
+ } catch (NoSuchAlgorithmException | KeyManagementException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private final Emitter.Listener connectedCallback = args -> callbackExecutor.execute(() -> {
+ Log.d(TAG, "Socket connected.");
+ });
+
+ private final Emitter.Listener iatfControlCallback = args -> callbackExecutor.execute(() -> {
+ synchronized (lockControl) {
+ JSONObject msg = (JSONObject) args[0];
+ Log.d(TAG, "iatf-control msg: " + msg.toString());
+ try {
+ controlMsg = msg.getString("type");
+ if (latch.getCount() != 0 && targetcontrolMsg.equals(controlMsg)) {
+ latch.countDown();
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ private final Emitter.Listener iatfWorkFlowCallback = args -> callbackExecutor.execute(() -> {
+ synchronized (lockWorkFlow) {
+ JSONObject workFlowMsg = (JSONObject) args[0];
+ Log.d(TAG, "iatf-workflow msg: " + workFlowMsg.toString());
+ try {
+ workFlowMsgData = workFlowMsg.getString("data");
+ workFlowMsgType = workFlowMsg.getString("type");
+ if (latch.getCount() != 0 && targetworkFlowMsgData.equals(workFlowMsgData)
+ && targetworkFlowMsgType.equals(workFlowMsgType)) {
+ latch.countDown();
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ public void connect(String taskId, String role) {
+ try {
+ setUpINSECURESSLContext();
+ IO.Options opt = new IO.Options();
+ opt.forceNew = true;
+ opt.reconnection = true;
+ opt.secure = true;
+ opt.query = "taskId=" + taskId + "&role=" + role + "&type=Android";
+ OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
+ if (sslContext != null) {
+ clientBuilder.sslSocketFactory(sslContext.getSocketFactory());
+ }
+ if (hostnameVerifier != null) {
+ clientBuilder.hostnameVerifier(hostnameVerifier);
+ }
+ OkHttpClient httpClient = clientBuilder.build();
+ opt.callFactory = httpClient;
+ opt.webSocketFactory = httpClient;
+ mSocket = IO.socket(IATF_SERVER, opt);
+ mSocket.on(Socket.EVENT_CONNECT, connectedCallback)
+ .on("iatf-control", iatfControlCallback)
+ .on("iatf-workflow", iatfWorkFlowCallback);
+ mSocket.connect();
+ } catch (URISyntaxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void disconnect(){
+ mSocket.disconnect();
+ }
+
+ public void notifyControlLock(String lockTpye, JSONObject lockDate) {
+ JSONObject msg = new JSONObject();
+ try {
+ msg.put("type", lockTpye);
+ msg.put("message",lockDate);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ mSocket.emit("iatf-control", msg);
+ }
+
+ public void notifyWorkflowLock(String lockTpye, String lockDate) {
+ JSONObject msg = new JSONObject();
+ try {
+ msg.put("type", lockTpye);
+ msg.put("data",lockDate);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ mSocket.emit("iatf-workflow", msg);
+ }
+
+ public void waitControlLock(String lock) {
+ synchronized (lockControl) {
+ targetcontrolMsg = lock;
+ latch = new CountDownLatch(1);
+ if (controlMsg.equals(lock)) {
+ latch.countDown();
+ }
+ }
+ assertTrue("can not get " + lock, waitLock());
+ }
+
+ public void waitWorkflowLock(String lockType, String lockData) {
+ synchronized (lockWorkFlow) {
+ targetworkFlowMsgType = lockType;
+ targetworkFlowMsgData = lockData;
+ latch = new CountDownLatch(1);
+ if (workFlowMsgType.equals(lockType) && workFlowMsgData.equals(lockData)) {
+ latch.countDown();
+ }
+ }
+ assertTrue(
+ "can not get workFlowType:" + lockType + " workFlowData:" + lockData,
+ waitLock());
+ }
+
+
+ private boolean waitLock() {
+ try {
+ if (latch.await(Integer.valueOf(IATF_WAIT_TIME), TimeUnit.MILLISECONDS)) {
+ return true;
+ } else {
+ Log.w(TAG, "Timeout on Resultable.getResult.");
+ }
+ } catch (InterruptedException e) {
+ Log.w(TAG, "InterruptedException during latch.await");
+ }
+ return false;
+ }
+}
diff --git a/test/util/src/main/resources/config.xml b/test/util/src/main/resources/config.xml
index f0eca6be..0dc5d368 100644
--- a/test/util/src/main/resources/config.xml
+++ b/test/util/src/main/resources/config.xml
@@ -6,4 +6,6 @@
place_holder
place_holder
place_holder
+ place_holder>
+ place_holder>