summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSho Amano <samano@xevo.com>2018-10-12 18:02:21 +0900
committerSho Amano <samano@xevo.com>2018-10-12 21:20:35 +0900
commit453c806c99fe0162fbd74f4627191de423577c45 (patch)
treec6a3a232d7be4248ababb873248eb903cc52e622
parentf362309c3c9532a46a972c64640dd366f2b6c6a3 (diff)
downloadsdl_android-453c806c99fe0162fbd74f4627191de423577c45.tar.gz
Add unit tests for WiFiSocketFactory
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/WiFiSocketFactoryTest.java352
1 files changed, 352 insertions, 0 deletions
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/WiFiSocketFactoryTest.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/WiFiSocketFactoryTest.java
new file mode 100644
index 000000000..0f2df233d
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/transport/WiFiSocketFactoryTest.java
@@ -0,0 +1,352 @@
+package com.smartdevicelink.test.transport;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.os.Build;
+import android.util.Log;
+
+import com.smartdevicelink.transport.utl.WiFiSocketFactory;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.net.SocketFactory;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+/**
+ * This is a unit test class for the WiFiSocketFactory class:
+ * {@link com.smartdevicelink.transport.utl.WiFiSocketFactory}
+ *
+ * Requires LOLLIPOP or later since the tests use android.net.NetworkCapabilities class
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class WiFiSocketFactoryTest extends TestCase {
+
+ private static final String TAG = WiFiSocketFactoryTest.class.getSimpleName();
+
+ private Context mMockContext;
+ private PackageManager mMockPackageManager;
+ private ConnectivityManager mMockConnMan;
+ private SocketFactory mMockSocketFactory; // this is the SocketFactory that creates mWiFiBoundSocket
+ private Socket mWiFiBoundSocket;
+
+ private enum FactoryRet {
+ RETURNS_NULL,
+ RETURNS_CORRECT_FACTORY,
+ RETURNS_ANOTHER_FACTORY,
+ }
+
+ private class MockNetworkConfig {
+ // true to make a null Network
+ boolean isNull;
+ // specify the transport type of the Network
+ int transportType;
+ // spcify the type of SocketFactory returned from this Network
+ FactoryRet factoryReturnValue;
+
+ MockNetworkConfig(boolean isNull, int transportType, FactoryRet factoryReturnValue) {
+ this.isNull = isNull;
+ this.transportType = transportType;
+ this.factoryReturnValue = factoryReturnValue;
+ }
+ }
+
+ private void setupMockNetworks(MockNetworkConfig[] configs) {
+ if (configs == null) {
+ when(mMockConnMan.getAllNetworks()).thenReturn(null);
+ return;
+ }
+
+ List<Network> networkList = new ArrayList<Network>(configs.length);
+
+ for (MockNetworkConfig config : configs) {
+ if (config.isNull) {
+ networkList.add(null);
+ continue;
+ }
+
+ Network network = mock(Network.class);
+
+ NetworkCapabilities networkCapabilities = createNetworkCapabilitiesWithTransport(config.transportType);
+ when(mMockConnMan.getNetworkCapabilities(network)).thenReturn(networkCapabilities);
+
+ SocketFactory factory = null;
+ switch (config.factoryReturnValue) {
+ case RETURNS_NULL:
+ break;
+ case RETURNS_CORRECT_FACTORY:
+ factory = mMockSocketFactory;
+ break;
+ case RETURNS_ANOTHER_FACTORY:
+ // create another mock SocketFactory instance
+ factory = mock(SocketFactory.class);
+ break;
+ }
+ when(network.getSocketFactory()).thenReturn(factory);
+
+ networkList.add(network);
+ }
+
+ when(mMockConnMan.getAllNetworks()).thenReturn(networkList.toArray(new Network[networkList.size()]));
+ }
+
+ private static NetworkCapabilities createNetworkCapabilitiesWithTransport(int transport) {
+ // Creates a dummy NetworkCapabilities instance.
+ // Since NetworkCapabilities class is 'final', we cannot create its mock. To create a dummy
+ // instance, here we use reflection to call its constructor and a method that are marked
+ // with "@hide".
+ // It is possible that these methods will not be available in a future version of Android.
+ // In that case we need to update our code accordingly.
+ Class<NetworkCapabilities> c = NetworkCapabilities.class;
+ try {
+ Method addTransportTypeMethod = c.getMethod("addTransportType", int.class);
+ addTransportTypeMethod.setAccessible(true);
+
+ NetworkCapabilities instance = c.getDeclaredConstructor().newInstance();
+ addTransportTypeMethod.invoke(instance, transport);
+ Log.e(TAG, "Yes successful");
+ return instance;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to create NetworkCapabilities instance using reflection: ", e);
+ return null;
+ }
+ }
+
+ // from https://stackoverflow.com/questions/40300469/mock-build-version-with-mockito
+ // and https://stackoverflow.com/questions/13755117/android-changing-private-static-final-field-using-java-reflection
+ private static void setFinalStatic(Field field, Object newValue) throws Exception {
+ field.setAccessible(true);
+// Field modifiersField = Field.class.getDeclaredField("modifiers");
+ // This call might fail on some devices (for example, Nexus 6 with Android 5.0.1).
+ // If that's the issue, we might want to introduce PowerMock.
+ Field modifiersField = Field.class.getDeclaredField("accessFlags");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
+ field.set(null, newValue);
+ }
+
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mMockContext = mock(Context.class);
+ mMockPackageManager = mock(PackageManager.class);
+ mMockConnMan = mock(ConnectivityManager.class);
+
+ when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
+ when(mMockContext.getPackageName()).thenReturn("dummyPackageName");
+ when(mMockContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))).thenReturn(mMockConnMan);
+
+ when(mMockPackageManager.checkPermission(eq(Manifest.permission.ACCESS_NETWORK_STATE), anyString())).thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ mMockSocketFactory = mock(SocketFactory.class);
+ mWiFiBoundSocket = new Socket();
+ when(mMockSocketFactory.createSocket()).thenReturn(mWiFiBoundSocket);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ // test the happy path
+ public void testWithWiFiNetwork() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_CELLULAR, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertEquals("Returned Socket should be created through SocketFactory", mWiFiBoundSocket, ret);
+ }
+
+ // test the case where SDK_INT is less than 21
+ public void testPriorToLollipop() throws Exception {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_CELLULAR, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ // simulate SDK_INT to less than LOLLIPOP
+ int previousSDKInt = Build.VERSION.SDK_INT;
+ setFinalStatic(Build.VERSION.class.getField("SDK_INT"), Build.VERSION_CODES.KITKAT_WATCH);
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ // make sure we revert our change
+ setFinalStatic(Build.VERSION.class.getField("SDK_INT"), previousSDKInt);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since it is not available prior to LOLLIPOP",
+ mWiFiBoundSocket, ret);
+ }
+
+ // test the case where we do not have ACCESS_NETWORK_STATE permission
+ public void testWithoutPermission() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ // simulate the case where required permission isn't available
+ when(mMockPackageManager.checkPermission(eq(Manifest.permission.ACCESS_NETWORK_STATE), anyString())).thenReturn(PackageManager.PERMISSION_DENIED);
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since we don't have required permission",
+ mWiFiBoundSocket, ret);
+ }
+
+ // test the case where context.getPackageManager() returns null
+ public void testPackageManagerNull() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ // simulate the case where ConnectivityManager isn't available
+ when(mMockContext.getPackageManager()).thenReturn(null);
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since PackageManager isn't available",
+ mWiFiBoundSocket, ret);
+ }
+
+ // test the case where getSystemService() returns null
+ public void testConnectivityManagerNull() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ // simulate the case where ConnectivityManager isn't available
+ when(mMockContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))).thenReturn(null);
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since ConnectivityManager isn't working",
+ mWiFiBoundSocket, ret);
+ }
+
+ // test the case where ConnectivityManager returns null for the network list
+ public void testNetworkListNull() {
+ setupMockNetworks(null);
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since Network list isn't available",
+ mWiFiBoundSocket, ret);
+ }
+
+ // test the case where the network list contains a null for Network instance
+ public void testNetworkListHasNull() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ // multiple Network instances in the list, the first one being NULL
+ new MockNetworkConfig(true, 0, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertEquals("Returned Socket should be created through SocketFactory", mWiFiBoundSocket, ret);
+ }
+
+ // test the case where the phone isn't connected to Wi-Fi network
+ public void testNoWiFiNetwork() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ // none of the instances has TRANSPORT_WIFI in their capabilities
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_CELLULAR, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_BLUETOOTH, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_VPN, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ });
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since Wi-Fi network isn't available",
+ mWiFiBoundSocket, ret);
+ }
+
+ // test the case where we get null for SocketFactory
+ public void testSocketFactoryNull() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_CELLULAR, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_NULL),
+ });
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since SocketFactory isn't available",
+ mWiFiBoundSocket, ret);
+ }
+
+ // test the case where we get a null for SocketFactory, then a valid one for another
+ public void testSocketFactoryNull2() {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_CELLULAR, FactoryRet.RETURNS_ANOTHER_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_NULL),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertEquals("Returned Socket should be created through SocketFactory", mWiFiBoundSocket, ret);
+ }
+
+ // test the case where we get an exception with SocketFactory.createSocket()
+ public void testFactoryReturnsException() throws IOException {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ when(mMockSocketFactory.createSocket()).thenThrow(new IOException("Dummy IOException for testing!"));
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertNotSame("Returned Socket shouldn't be created through SocketFactory since it throws an IOException",
+ mWiFiBoundSocket, ret);
+ }
+
+ // Test the case we get multiple Network instances with Wi-Fi transport, and the SocketFactory of
+ // the first one throws Exception and the other one succeeds.
+ // This is to simulate Samsung Galaxy S9.
+ public void testFactoryReturnsException2() throws IOException {
+ setupMockNetworks(new MockNetworkConfig[] {
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ new MockNetworkConfig(false, NetworkCapabilities.TRANSPORT_WIFI, FactoryRet.RETURNS_CORRECT_FACTORY),
+ });
+
+ when(mMockSocketFactory.createSocket()).thenThrow(new IOException("Dummy IOException for testing!"))
+ .thenReturn(mWiFiBoundSocket);
+
+ Socket ret = WiFiSocketFactory.createSocket(mMockContext);
+
+ assertNotNull("createSocket() should always return a Socket instance", ret);
+ assertEquals("Returned Socket should be created through SocketFactory", mWiFiBoundSocket, ret);
+ }
+}