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>