From 3e70c210318beec4147c3c2b690226ed30b9bec9 Mon Sep 17 00:00:00 2001 From: Vineet Garg Date: Sat, 13 Jun 2015 12:36:22 +0530 Subject: [PATCH] Added LanLinkTest and LanLinkProviderTest --- .../kde/kdeconnect/LanLinkProviderTest.java | 250 +++++++++++++++++ tests/org/kde/kdeconnect/LanLinkTest.java | 265 ++++++++++++++++++ 2 files changed, 515 insertions(+) create mode 100644 tests/org/kde/kdeconnect/LanLinkProviderTest.java create mode 100644 tests/org/kde/kdeconnect/LanLinkTest.java diff --git a/tests/org/kde/kdeconnect/LanLinkProviderTest.java b/tests/org/kde/kdeconnect/LanLinkProviderTest.java new file mode 100644 index 00000000..fe7863a4 --- /dev/null +++ b/tests/org/kde/kdeconnect/LanLinkProviderTest.java @@ -0,0 +1,250 @@ +/* + * Copyright 2015 Vineet Garg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package org.kde.kdeconnect; + +import android.support.v4.util.LongSparseArray; +import android.test.AndroidTestCase; +import android.util.Log; + +import org.apache.mina.core.service.IoHandler; +import org.apache.mina.core.service.IoHandlerAdapter; +import org.apache.mina.core.session.IoSession; +import org.apache.mina.transport.socket.nio.NioDatagramAcceptor; +import org.apache.mina.transport.socket.nio.NioSocketAcceptor; +import org.kde.kdeconnect.Backends.LanBackend.LanLink; +import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.InputStream; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.HashMap; + +public class LanLinkProviderTest extends AndroidTestCase { + + private NioSocketAcceptor tcpAcceptor = null; + private NioDatagramAcceptor udpAcceptor = null; + private LanLinkProvider linkProvider; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath()); + + linkProvider = new LanLinkProvider(getContext()); + + try { + Field field = LanLinkProvider.class.getDeclaredField("tcpAcceptor"); + field.setAccessible(true); + tcpAcceptor = (NioSocketAcceptor)field.get(linkProvider); + assertNotNull(tcpAcceptor); + }catch (Exception e){ + fail("Error getting tcpAcceptor from LanLinkProvider"); + } + + try{ + Field field = LanLinkProvider.class.getDeclaredField("udpAcceptor"); + field.setAccessible(true); + udpAcceptor = (NioDatagramAcceptor)field.get(linkProvider); + assertNotNull(udpAcceptor); + }catch (Exception e){ + fail("Error getting udp acceptor from LanLinkProvider"); + } + + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + + tcpAcceptor.dispose(); + udpAcceptor.dispose(); + } + + public void testTcpAcceptor(){ + + assertNotNull(tcpAcceptor.getHandler()); + assertEquals(tcpAcceptor.getSessionConfig().isKeepAlive(), true); + assertEquals(tcpAcceptor.getSessionConfig().isReuseAddress(), true); + assertNotNull(tcpAcceptor.getFilterChain().get("codec")); + + } + + public void testUdpAcceptor(){ + + assertNull(udpAcceptor.getHandler()); + assertEquals(udpAcceptor.getSessionConfig().isReuseAddress(), true); + assertNotNull(udpAcceptor.getFilterChain().get("codec")); + } + + public void testOnStart() throws Exception{ + + IoSession session = Mockito.mock(IoSession.class); + Mockito.when(session.getId()).thenReturn(12345l); + Mockito.when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(5000)); + + linkProvider.onStart(); + + assertNotNull(udpAcceptor.getHandler()); + assertEquals(udpAcceptor.getLocalAddress().getPort(), 1714); + } + + public void testUdpPackageReceived() throws Exception { + + final int port = 5000; + + NetworkPackage networkPackage = Mockito.mock(NetworkPackage.class); + Mockito.when(networkPackage.getType()).thenReturn("kdeconnect.identity"); + Mockito.when(networkPackage.getString("deviceId")).thenReturn("testDevice"); + Mockito.when(networkPackage.getString("deviceName")).thenReturn("Test Device"); + Mockito.when(networkPackage.getInt("protocolVersion")).thenReturn(NetworkPackage.ProtocolVersion); + Mockito.when(networkPackage.getString("deviceType")).thenReturn("phone"); + Mockito.when(networkPackage.getInt("tcpPort")).thenReturn(port); + + final String serialized = "{\"type\":\"kdeconnect.identity\",\"id\":12345,\"body\":{\"deviceName\":\"Test Device\",\"deviceType\":\"phone\",\"deviceId\":\"testDevice\",\"protocolVersion\":5,\"tcpPort\": "+ port +"}}"; + Mockito.when(networkPackage.serialize()).thenReturn(serialized); + + // Mocking udp session + IoSession session = Mockito.mock(IoSession.class); + Mockito.when(session.getId()).thenReturn(12345l); + Mockito.when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(port)); + + + // Making a server socket, so that original LanLinkProvider can connect to it when it receives our fake package + final ServerSocket serverSocket = new ServerSocket(); + serverSocket.bind(new InetSocketAddress(port)); + + final Thread thread = new Thread (new Runnable() { + @Override + public void run() { + try { + Socket socket = serverSocket.accept(); + InputStream inputStream = socket.getInputStream(); + while (true) { + if (inputStream.available() != 0) { + // Data received from socket should be an identity package + byte[] inputData = new byte[inputStream.available()]; + inputStream.read(inputData); + + NetworkPackage receivedPackage = NetworkPackage.unserialize(new String(inputData)); + NetworkPackage identityPackage = NetworkPackage.createIdentityPackage(getContext()); + + // If any assertion fails, its output will be in logcat, not on test case thread anymore + assertEquals(receivedPackage.getType(), identityPackage.getType()); + assertEquals(receivedPackage.getString("deviceName"), identityPackage.getString("deviceName")); + assertEquals(receivedPackage.getString("deviceId"), identityPackage.getString("deviceId")); + assertEquals(receivedPackage.getInt("protocolVersion"), identityPackage.getInt("protocolVersion")); + + serverSocket.close(); + // Socket not closed to ensure visibleComputers contains entry for testDevice + break; + } + } + + }catch (Exception e){ + assertEquals("Exception in thread",1,5); + } + } + }); + + try { + thread.start(); + linkProvider.onStart(); + udpAcceptor.getHandler().messageReceived(session, networkPackage.serialize()); + }catch (Exception e){ + throw e; + } + + // Wait 1 secs for our server, and then end test + thread.join(1 * 1000); + + // visibleComputers should contain an entry for testDevice + HashMap visibleComputers; + try { + Field field = LanLinkProvider.class.getDeclaredField("visibleComputers"); + field.setAccessible(true); + visibleComputers = (HashMap)field.get(linkProvider); + }catch (Exception e){ + throw e; + } + assertNotNull(visibleComputers.get("testDevice")); + + } + + + public void testTcpIdentityPackageReceived() throws Exception{ + + IoSession session = Mockito.mock(IoSession.class); + Mockito.when(session.getId()).thenReturn(12345l); + + NetworkPackage networkPackage = Mockito.mock(NetworkPackage.class); + Mockito.when(networkPackage.getType()).thenReturn("kdeconnect.identity"); + Mockito.when(networkPackage.getString("deviceId")).thenReturn("testDevice"); + Mockito.when(networkPackage.getString("deviceName")).thenReturn("Test Device"); + Mockito.when(networkPackage.getInt("protocolVersion")).thenReturn(NetworkPackage.ProtocolVersion); + Mockito.when(networkPackage.getString("deviceType")).thenReturn("phone"); + + String serialized = "{\"type\":\"kdeconnect.identity\",\"id\":12345,\"body\":{\"deviceName\":\"Test Device\",\"deviceType\":\"phone\",\"deviceId\":\"testDevice\",\"protocolVersion\":5}}"; + Mockito.when(networkPackage.serialize()).thenReturn(serialized); + + try { + tcpAcceptor.getHandler().messageReceived(session, networkPackage.serialize()); + }catch (Exception e){ + throw e; + } + + LongSparseArray nioSessions; + try { + Field field = LanLinkProvider.class.getDeclaredField("nioSessions"); + field.setAccessible(true); + nioSessions = (LongSparseArray)field.get(linkProvider); + }catch (Exception e){ + throw e; + } + assertNotNull(nioSessions.get(12345l)); + + + HashMap visibleComputers; + try { + Field field = LanLinkProvider.class.getDeclaredField("visibleComputers"); + field.setAccessible(true); + visibleComputers = (HashMap)field.get(linkProvider); + }catch (Exception e){ + throw e; + } + assertNotNull(visibleComputers.get("testDevice")); + + + // Testing session closed + try { + tcpAcceptor.getHandler().sessionClosed(session); + }catch (Exception e){ + throw e; + } + assertNull(nioSessions.get(12345l)); + assertNull(visibleComputers.get("testDevice")); + } +} diff --git a/tests/org/kde/kdeconnect/LanLinkTest.java b/tests/org/kde/kdeconnect/LanLinkTest.java new file mode 100644 index 00000000..d3d94c9c --- /dev/null +++ b/tests/org/kde/kdeconnect/LanLinkTest.java @@ -0,0 +1,265 @@ +/* + * Copyright 2015 Vineet Garg + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ + +package org.kde.kdeconnect; + +import android.test.AndroidTestCase; +import android.util.Log; + +import org.apache.mina.core.future.WriteFuture; +import org.apache.mina.core.session.IoSession; +import org.json.JSONObject; +import org.kde.kdeconnect.Backends.LanBackend.LanLink; +import org.kde.kdeconnect.Backends.LanBackend.LanLinkProvider; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.Socket; + +public class LanLinkTest extends AndroidTestCase { + + LanLink lanLink; + IoSession session; + Device.SendPackageStatusCallback callback; + + WriteFuture writeFutureSuccess, writeFutureFailure; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath()); + + LanLinkProvider linkProvider = Mockito.mock(LanLinkProvider.class); + Mockito.when(linkProvider.getName()).thenReturn("LanLinkProvider"); + + session = Mockito.mock(IoSession.class); + Mockito.when(session.getId()).thenReturn(12345l); + Mockito.when(session.getRemoteAddress()).thenReturn(new InetSocketAddress(5000)); + + callback = Mockito.mock(Device.SendPackageStatusCallback.class); + Mockito.doNothing().when(callback).sendSuccess(); + Mockito.doNothing().when(callback).sendProgress(Mockito.any(Integer.class)); + Mockito.doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + throw (Throwable) invocationOnMock.getArguments()[0]; + } + }).when(callback).sendFailure(Mockito.any(Throwable.class)); + + writeFutureSuccess = Mockito.mock(WriteFuture.class); + Mockito.when(writeFutureSuccess.isWritten()).thenReturn(true); + Mockito.when(writeFutureSuccess.getSession()).thenReturn(session); + Mockito.when(writeFutureSuccess.isDone()).thenReturn(true); + + writeFutureFailure = Mockito.mock(WriteFuture.class); + Mockito.when(writeFutureFailure.isWritten()).thenReturn(false); + Mockito.when(writeFutureFailure.getSession()).thenReturn(session); + Mockito.when(writeFutureFailure.isDone()).thenReturn(true); + Mockito.when(writeFutureFailure.getException()).thenReturn(new RuntimeException()); + + lanLink = new LanLink(session, "testDevice", linkProvider); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + public void testSendPackageSuccess(){ + + NetworkPackage testPackage = Mockito.mock(NetworkPackage.class); + Mockito.when(testPackage.getType()).thenReturn("kdeconnect.test"); + Mockito.when(testPackage.getBoolean("isTesting")).thenReturn(true); + Mockito.when(testPackage.getString("testName")).thenReturn("testSendPackageSuccess"); + Mockito.when(testPackage.serialize()).thenReturn("{\"id\":123,\"type\":\"kdeconnect.test\",\"body\":{\"isTesting\":true,\"testName\":\"testSendPackageSuccess\"}}"); + + Mockito.when(session.write(testPackage.serialize())).thenReturn(writeFutureSuccess); + lanLink.sendPackage(testPackage, callback); + } + + public void testSendPackageFail(){ + + NetworkPackage testPackage = Mockito.mock(NetworkPackage.class); + Mockito.when(testPackage.getType()).thenReturn("kdeconnect.test"); + Mockito.when(testPackage.getBoolean("isTesting")).thenReturn(true); + Mockito.when(testPackage.getString("testName")).thenReturn("testSendPackageFail"); + Mockito.when(testPackage.serialize()).thenReturn("{\"id\":123,\"type\":\"kdeconnect.test\",\"body\":{\"isTesting\":true,\"testName\":\"testSendPackageFail\"}}"); + + Mockito.when(session.write(testPackage.serialize())).thenReturn(writeFutureFailure); + try { + lanLink.sendPackage(testPackage, callback); + }catch (RuntimeException r){ + // Do nothing, test is fine, callback should throw this exception + }catch (Exception e){ + // This should not happen + throw e; + } + + + } + + + public void testSendPayload() throws Exception{ + + class Downloader extends Thread { + + NetworkPackage np; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + public void setNetworkPackage(NetworkPackage networkPackage){ + this.np = networkPackage; + } + + public ByteArrayOutputStream getOutputStream(){ + return outputStream; + } + + @Override + public void run(){ + try { + + Socket socket = null; + try { + socket = new Socket(); + int tcpPort = np.getPayloadTransferInfo().getInt("port"); + InetSocketAddress address = (InetSocketAddress)session.getRemoteAddress(); + socket.connect(new InetSocketAddress(address.getAddress(), tcpPort)); + np.setPayload(socket.getInputStream(), np.getPayloadSize()); + } catch (Exception e) { + try { socket.close(); } catch(Exception ignored) { throw ignored; } + e.printStackTrace(); + Log.e("KDE/LanLinkTest", "Exception connecting to remote socket"); + throw e; + } + + final InputStream input = np.getPayload(); + final long fileLength = np.getPayloadSize(); + + byte data[] = new byte[1024]; + long progress = 0, prevProgressPercentage = 0; + int count; + while ((count = input.read(data)) >= 0) { + progress += count; + outputStream.write(data, 0, count); + if (fileLength > 0) { + if (progress >= fileLength) break; + long progressPercentage = (progress * 100 / fileLength); + if (progressPercentage != prevProgressPercentage) { + prevProgressPercentage = progressPercentage; + } + } + + } + outputStream.close(); + input.close(); + + } catch(Exception e) { + Log.e("Downloader Test", "Exception"); + e.printStackTrace(); + } + } + } + + + final Downloader downloader = new Downloader(); + + // Using byte array for payload, try to use input stream as used in real device + String dataString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."+ + " Cras vel erat et ante fringilla tristique. Sed consequat ligula at interdum "+ + "rhoncus. Integer semper enim felis, id sodales tellus aliquet eget."+ + " Sed fringilla ac metus eget dictum. Aliquam euismod non sem sit"+ + " amet dapibus. Interdum et malesuada fames ac ante ipsum primis "+ + "in faucibus. Nam et ligula placerat, varius justo eu, convallis "+ + "lorem. Nam consequat consequat tortor et gravida. Praesent "+ + "ultricies tortor eget ex elementum gravida. Suspendisse aliquet "+ + "erat a orci feugiat dignissim."; + + // reallyLongString contains dataString 16 times + String reallyLongString = dataString + dataString; + reallyLongString = reallyLongString + reallyLongString; + reallyLongString = reallyLongString + reallyLongString; + reallyLongString = reallyLongString + reallyLongString; + + final byte[] data = reallyLongString.getBytes(); + + final JSONObject sharePackageJson = new JSONObject("{\"id\":123,\"body\":{\"filename\":\"data.txt\"},\"payloadTransferInfo\":{},\"payloadSize\":8720,\"type\":\"kdeconnect.share\"}"); + + // Mocking share package + final NetworkPackage sharePackage = Mockito.mock(NetworkPackage.class); + Mockito.when(sharePackage.getType()).thenReturn("kdeconnect.share"); + Mockito.when(sharePackage.hasPayload()).thenReturn(true); + Mockito.when(sharePackage.hasPayloadTransferInfo()).thenReturn(true); + Mockito.doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + return sharePackageJson.toString(); + } + }).when(sharePackage).serialize(); + Mockito.when(sharePackage.getPayload()).thenReturn(new ByteArrayInputStream(data)); + Mockito.when(sharePackage.getPayloadSize()).thenReturn((long) data.length); + Mockito.doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + return sharePackageJson.getJSONObject("payloadTransferInfo"); + } + }).when(sharePackage).getPayloadTransferInfo(); + Mockito.doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + JSONObject object = (JSONObject)invocationOnMock.getArguments()[0]; + + sharePackageJson.put("payloadTransferInfo", object); + return null; + } + }).when(sharePackage).setPayloadTransferInfo(Mockito.any(JSONObject.class)); + + Mockito.doAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocationOnMock) throws Throwable { + + String stringNetworkPackage = (String)invocationOnMock.getArguments()[0]; + final NetworkPackage np = NetworkPackage.unserialize(stringNetworkPackage); + + downloader.setNetworkPackage(np); + downloader.start(); + + return writeFutureSuccess; + } + }).when(session).write(Mockito.anyString()); + + lanLink.sendPackage(sharePackage, callback); + + try { + // Wait 1 secs for downloader to finish (if some error, it will continue and assert will fail) + downloader.join(1*1000); + }catch (Exception e){ + e.printStackTrace(); + throw e; + } + assertEquals(new String(data), new String(downloader.getOutputStream().toByteArray())); + + } +}