summaryrefslogtreecommitdiff
path: root/app/src/test/java/se
diff options
context:
space:
mode:
authorfupduck <fupduck@riseup.net>2018-01-11 06:37:01 -0800
committerfupduck <fupduck@riseup.net>2018-01-11 06:37:01 -0800
commit68d6eb91436d0d145fd340056fd8000f7dd1ff34 (patch)
tree2f37b74f259915d1b04facd0d0f59856f112f8b8 /app/src/test/java/se
parent67ff3447f10c43770dc9ee4dccf358321063d131 (diff)
parent1e94e6e1403d97e47119318bd43b173ef20658b1 (diff)
Merge branch '8773_certificate_pinning' into '0.9.8'
8773 certificate pinning See merge request leap/bitmask_android!21
Diffstat (limited to 'app/src/test/java/se')
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/TestUtils.java26
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java10
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java337
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java10
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java86
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java75
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java89
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java97
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java165
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java434
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java208
11 files changed, 1501 insertions, 36 deletions
diff --git a/app/src/test/java/se/leap/bitmaskclient/TestUtils.java b/app/src/test/java/se/leap/bitmaskclient/TestUtils.java
deleted file mode 100644
index 96b11df6..00000000
--- a/app/src/test/java/se/leap/bitmaskclient/TestUtils.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package se.leap.bitmaskclient;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-
-/**
- * Created by cyberta on 08.10.17.
- */
-
-public class TestUtils {
-
- public static String getInputAsString(InputStream fileAsInputStream) throws IOException {
- BufferedReader br = new BufferedReader(new InputStreamReader(fileAsInputStream));
- StringBuilder sb = new StringBuilder();
- String line = br.readLine();
- while (line != null) {
- sb.append(line);
- line = br.readLine();
- }
-
- return sb.toString();
- }
-
-}
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
index ea212480..4726cab7 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
@@ -16,7 +16,7 @@ import java.io.IOException;
import se.leap.bitmaskclient.Constants;
import se.leap.bitmaskclient.Provider;
-import se.leap.bitmaskclient.TestUtils;
+import se.leap.bitmaskclient.testutils.TestSetupHelper;
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -61,17 +61,17 @@ public class GatewaysManagerTest {
@Test
public void testFromEipServiceJson_ignoreDuplicateGateways() throws Exception {
- String eipServiceJson = TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-two-gateways.json"));
+ String eipServiceJson = TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-two-gateways.json"));
gatewaysManager.fromEipServiceJson(new JSONObject(eipServiceJson));
assertEquals(2, gatewaysManager.size());
- eipServiceJson = TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-one-gateway.json"));
+ eipServiceJson = TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-one-gateway.json"));
gatewaysManager.fromEipServiceJson(new JSONObject(eipServiceJson));
assertEquals(2, gatewaysManager.size());
}
@Test
public void testClearGatewaysAndProfiles_resetGateways() throws Exception {
- String eipServiceJson = TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-two-gateways.json"));
+ String eipServiceJson = TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("eip-service-two-gateways.json"));
gatewaysManager.fromEipServiceJson(new JSONObject(eipServiceJson));
assertEquals(2, gatewaysManager.size());
gatewaysManager.clearGatewaysAndProfiles();
@@ -79,7 +79,7 @@ public class GatewaysManagerTest {
}
private String getJsonStringFor(String filename) throws IOException {
- return TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream(filename));
+ return TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream(filename));
}
} \ No newline at end of file
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
new file mode 100644
index 00000000..9ca90b17
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
@@ -0,0 +1,337 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package se.leap.bitmaskclient.eip;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+
+import se.leap.bitmaskclient.ConfigHelper;
+import se.leap.bitmaskclient.Provider;
+import se.leap.bitmaskclient.ProviderAPI;
+import se.leap.bitmaskclient.ProviderApiConnector;
+import se.leap.bitmaskclient.ProviderApiManager;
+import se.leap.bitmaskclient.ProviderApiManagerBase;
+import se.leap.bitmaskclient.testutils.MockSharedPreferences;
+
+import static se.leap.bitmaskclient.ProviderAPI.ERRORS;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_NOK;
+import static se.leap.bitmaskclient.ProviderAPI.PROVIDER_OK;
+import static se.leap.bitmaskclient.ProviderAPI.RESULT_KEY;
+import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_UPDATED_CERTIFICATE;
+import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockBundle;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockClientGenerator;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockFingerprintForCertificate;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockIntent;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockProviderApiConnector;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockResources;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockResultReceiver;
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.mockTextUtils;
+
+
+/**
+ * Created by cyberta on 04.01.18.
+ */
+
+@RunWith(PowerMockRunner.class)
+@PrepareForTest({ProviderApiManager.class, TextUtils.class, ConfigHelper.class, ProviderApiConnector.class})
+public class ProviderApiManagerTest {
+
+ private SharedPreferences mockPreferences;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Resources mockResources;
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private Context mockContext;
+
+ private ProviderApiManager providerApiManager;
+
+ class TestProviderApiServiceCallback implements ProviderApiManagerBase.ProviderApiServiceCallback {
+
+ //Intent expectedIntent;
+ TestProviderApiServiceCallback(/*Intent expectedIntent*/) {
+ //this.expectedIntent = expectedIntent;
+ }
+
+ @Override
+ public void broadcastProgress(Intent intent) {
+ //assertEquals("expected intent: ", expectedIntent, intent);
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+
+ Bundle bundle = mockBundle();
+ PowerMockito.whenNew(Bundle.class).withAnyArguments().thenReturn(bundle);
+ Intent intent = mockIntent();
+ PowerMockito.whenNew(Intent.class).withAnyArguments().thenReturn(intent);
+ mockTextUtils();
+ mockPreferences = new MockSharedPreferences();
+ mockResources = mockResources(getClass().getClassLoader().getResourceAsStream("error_messages.json"));
+ }
+
+
+ @Test
+ public void test_handleIntentSetupProvider_noProviderMainURL() {
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errors\":\"It doesn't seem to be a Bitmask provider.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "");
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_happyPath_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(NO_ERROR);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, true);
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+ parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem")));
+ parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json")));
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_happyPath_no_preseededProviderAndCA() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(NO_ERROR);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, true);
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_happyPath_storedProviderAndCAFromPreviousSetup() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(NO_ERROR);
+ mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
+ mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, true);
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_preseededProviderAndCA_failedCAPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495");
+ mockProviderApiConnector(NO_ERROR);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+ parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem")));
+ parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json")));
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_no_preseededProviderAndCA_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495");
+ mockProviderApiConnector(NO_ERROR);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_failedPinning() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29495");
+ mockProviderApiConnector(NO_ERROR);
+ mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
+ mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_CERTIFICATE_PINNING\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+
+ @Test
+ public void test_handleIntentSetupProvider_preseededProviderAndCA_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockProviderApiConnector(NO_ERROR);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+ parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("outdated_cert.pem")));
+ parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json")));
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_outdatedCertificate() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockProviderApiConnector(NO_ERROR);
+ mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
+ mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("outdated_cert.pem"))).apply();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is expired. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_preseededProviderAndCA_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);
+
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+ parameters.putString(Provider.CA_CERT, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem")));
+ parameters.putString(Provider.KEY, getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json")));
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_storedProviderAndCAFromPreviousSetup_ValidCertificateButUpdatedCertificateOnServerSide() throws IOException, CertificateEncodingException, NoSuchAlgorithmException {
+ mockFingerprintForCertificate("a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_CASE_UPDATED_CERTIFICATE);
+ mockPreferences.edit().putString(Provider.KEY + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"))).apply();
+ mockPreferences.edit().putString(Provider.CA_CERT + ".riseup.net", getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"))).apply();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback());
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_INVALID_CERTIFICATE\",\"errors\":\"Stored provider certificate is invalid. You can either update Bitmask (recommended) or update the provider certificate using a commercial CA certificate.\"}");
+
+ Intent provider_API_command = mockIntent();
+ Bundle parameters = mockBundle();
+ parameters.putString(Provider.MAIN_URL, "https://riseup.net");
+
+ provider_API_command.setAction(ProviderAPI.SET_UP_PROVIDER);
+ provider_API_command.putExtra(ProviderAPI.PARAMETERS, parameters);
+ provider_API_command.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK, expectedResult));
+
+ providerApiManager.handleIntent(provider_API_command);
+ }
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java
index 7e60edb9..8c8cdb61 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/VpnConfigGeneratorTest.java
@@ -4,7 +4,7 @@ import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
-import se.leap.bitmaskclient.TestUtils;
+import se.leap.bitmaskclient.testutils.TestSetupHelper;
import static junit.framework.Assert.assertTrue;
@@ -217,13 +217,13 @@ public class VpnConfigGeneratorTest {
@Before
public void setUp() throws Exception {
- generalConfig = new JSONObject(TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream("general_configuration.json")));
- secrets = new JSONObject(TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream("secrets.json")));
+ generalConfig = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("general_configuration.json")));
+ secrets = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("secrets.json")));
}
@Test
public void testGenerate_tcp_udp() throws Exception {
- gateway = new JSONObject(TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_tcp_udp.json")));
+ gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_tcp_udp.json")));
vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway);
String vpnConfig = vpnConfigGenerator.generate();
@@ -232,7 +232,7 @@ public class VpnConfigGeneratorTest {
@Test
public void testGenerate_udp_tcp() throws Exception {
- gateway = new JSONObject(TestUtils.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_udp_tcp.json")));
+ gateway = new JSONObject(TestSetupHelper.getInputAsString(getClass().getClassLoader().getResourceAsStream("gateway_udp_tcp.json")));
vpnConfigGenerator = new VpnConfigGenerator(generalConfig, secrets, gateway);
String vpnConfig = vpnConfigGenerator.generate();
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java
new file mode 100644
index 00000000..9069661f
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java
@@ -0,0 +1,86 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.testutils.BackendMockResponses;
+
+import java.io.IOException;
+
+/**
+ * Created by cyberta on 10.01.18.
+ */
+
+public class BackendMockProvider {
+ /**
+ * This enum can be useful to provide different responses from a mocked ProviderApiConnector
+ * in order to test different error scenarios
+ */
+ public enum TestBackendErrorCase {
+ NO_ERROR,
+ ERROR_CASE_UPDATED_CERTIFICATE,
+ ERROR_NO_RESPONSE_BODY, // => NullPointerException
+ ERROR_DNS_RESOLUTION_ERROR, // => UnkownHostException
+ ERROR_SOCKET_TIMEOUT, // => SocketTimeoutException
+ ERROR_WRONG_PROTOCOL, // => MalformedURLException
+ ERROR_CERTIFICATE_INVALID, // => SSLHandshakeException
+ ERROR_WRONG_PORT, // => ConnectException
+ ERROR_PAYLOAD_MISSING, // => IllegalArgumentException
+ ERROR_TLS_1_2_NOT_SUPPORTED, // => UnknownServiceException
+ ERROR_UNKNOWN_IO_EXCEPTION, // => IOException
+ ERROR_NO_ACCESS,
+ ERROR_INVALID_SESSION_TOKEN,
+ ERROR_NO_CONNECTION,
+ ERROR_WRONG_SRP_CREDENTIALS
+ }
+
+
+ public static void provideBackendResponsesFor(TestBackendErrorCase errorCase) throws IOException {
+ switch (errorCase) {
+
+ case NO_ERROR:
+ new NoErrorBackendResponse();
+ break;
+ case ERROR_CASE_UPDATED_CERTIFICATE:
+ new UpdatedCertificateBackendResponse();
+ break;
+ case ERROR_NO_RESPONSE_BODY:
+ break;
+ case ERROR_DNS_RESOLUTION_ERROR:
+ break;
+ case ERROR_SOCKET_TIMEOUT:
+ break;
+ case ERROR_WRONG_PROTOCOL:
+ break;
+ case ERROR_CERTIFICATE_INVALID:
+ break;
+ case ERROR_WRONG_PORT:
+ break;
+ case ERROR_PAYLOAD_MISSING:
+ break;
+ case ERROR_TLS_1_2_NOT_SUPPORTED:
+ break;
+ case ERROR_UNKNOWN_IO_EXCEPTION:
+ break;
+ case ERROR_NO_ACCESS:
+ break;
+ case ERROR_INVALID_SESSION_TOKEN:
+ break;
+ case ERROR_NO_CONNECTION:
+ break;
+ case ERROR_WRONG_SRP_CREDENTIALS:
+ break;
+ }
+ }
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java
new file mode 100644
index 00000000..98224019
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BaseBackendResponse.java
@@ -0,0 +1,75 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.testutils.BackendMockResponses;
+
+import android.util.Pair;
+
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+
+import okhttp3.OkHttpClient;
+import se.leap.bitmaskclient.ProviderApiConnector;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+
+/**
+ * Created by cyberta on 10.01.18.
+ */
+
+public abstract class BaseBackendResponse {
+
+ private Answer<String> answerRequestStringFromServer;
+ private Answer<Boolean> answerCanConnect;
+ private Answer<Boolean> answerDelete;
+
+ public BaseBackendResponse() throws IOException {
+ mockStatic(ProviderApiConnector.class);
+ this.answerRequestStringFromServer = getAnswerForRequestStringFromServer();
+ this.answerCanConnect = getAnswerForCanConnect();
+ this.answerDelete = getAnswerForDelete();
+
+ responseOnRequestStringFromServer();
+ responseOnCanConnect();
+ responseOnDelete();
+
+ }
+
+ public abstract Answer<String> getAnswerForRequestStringFromServer();
+ public abstract Answer<Boolean> getAnswerForCanConnect();
+ public abstract Answer<Boolean> getAnswerForDelete();
+
+
+ public void responseOnRequestStringFromServer() throws IOException, RuntimeException {
+ Mockito.when(ProviderApiConnector.requestStringFromServer(anyString(), anyString(), nullable(String.class), ArgumentMatchers.<Pair<String,String>>anyList(), any(OkHttpClient.class))).
+ thenAnswer(answerRequestStringFromServer);
+ }
+
+ public void responseOnCanConnect() throws IOException, RuntimeException {
+ Mockito.when(ProviderApiConnector.canConnect(any(OkHttpClient.class), anyString())).thenAnswer(answerCanConnect);
+ }
+
+ public void responseOnDelete() throws IOException, RuntimeException {
+ Mockito.when(ProviderApiConnector.delete(any(OkHttpClient.class), anyString())).thenAnswer(answerDelete);
+ }
+
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java
new file mode 100644
index 00000000..fa318e42
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/NoErrorBackendResponse.java
@@ -0,0 +1,89 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.testutils.BackendMockResponses;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
+
+/**
+ * Created by cyberta on 10.01.18.
+ */
+
+public class NoErrorBackendResponse extends BaseBackendResponse {
+ public NoErrorBackendResponse() throws IOException {
+ super();
+ }
+
+ @Override
+ public Answer<String> getAnswerForRequestStringFromServer() {
+ return new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ String url = (String) invocation.getArguments()[0];
+ String requestMethod = (String) invocation.getArguments()[1];
+ String jsonPayload = (String) invocation.getArguments()[2];
+
+ if (url.contains("/provider.json")) {
+ //download provider json
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"));
+ } else if (url.contains("/ca.crt")) {
+ //download provider ca cert
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"));
+ } else if (url.contains("config/eip-service.json")) {
+ // download provider service json containing gateways, locations and openvpn settings
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json"));
+ } else if (url.contains("/users.json")) {
+ //create new user
+ //TODO: implement me
+ } else if (url.contains("/sessions.json")) {
+ //srp auth: sendAToSRPServer
+ //TODO: implement me
+ } else if (url.contains("/sessions/parmegvtest10.json")){
+ //srp auth: sendM1ToSRPServer
+ //TODO: implement me
+ }
+
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForCanConnect() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ return true;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForDelete() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ return true;
+ }
+ };
+ }
+
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java
new file mode 100644
index 00000000..232649a1
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/UpdatedCertificateBackendResponse.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.testutils.BackendMockResponses;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+
+import javax.net.ssl.SSLHandshakeException;
+
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
+
+/**
+ * Created by cyberta on 10.01.18.
+ */
+
+public class UpdatedCertificateBackendResponse extends BaseBackendResponse {
+ static volatile boolean wasCACertCalled = false;
+
+
+ public UpdatedCertificateBackendResponse() throws IOException {
+ super();
+ }
+
+ @Override
+ public Answer<String> getAnswerForRequestStringFromServer() {
+ return new Answer<String>() {
+
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ String url = (String) invocation.getArguments()[0];
+
+ if (url.contains("/provider.json")) {
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ //download provider json
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.json"));
+ } else if (url.contains("/ca.crt")) {
+ //download provider ca cert
+ wasCACertCalled = true;
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("updated_cert.pem"));
+ } else if (url.contains("config/eip-service.json")) {
+ // download provider service json containing gateways, locations and openvpn settings
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.service.json"));
+ }
+
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForCanConnect() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ return true;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForDelete() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ if (!wasCACertCalled) {
+ throw new SSLHandshakeException("Updated certificate on server side");
+ }
+ return true;
+ }
+ };
+ }
+
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java
new file mode 100644
index 00000000..af35af31
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockSharedPreferences.java
@@ -0,0 +1,165 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package se.leap.bitmaskclient.testutils;
+
+import android.content.SharedPreferences;
+import android.support.annotation.Nullable;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Created by cyberta on 09.01.18.
+ */
+
+public class MockSharedPreferences implements SharedPreferences {
+ HashMap<String, String> mockedStringPrefs = new HashMap<>();
+ HashMap<String, Integer> mockedIntPrefs = new HashMap<>();
+ HashMap<String, Boolean> mockedBooleanPrefs = new HashMap<>();
+
+ @Override
+ public Map<String, ?> getAll() {
+ return null;
+ }
+
+ @Nullable
+ @Override
+ public String getString(String key, @Nullable String defValue) {
+ String value = mockedStringPrefs.get(key);
+ return value != null ? value : defValue;
+ }
+
+ @Nullable
+ @Override
+ public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
+ return null;
+ }
+
+ @Override
+ public int getInt(String key, int defValue) {
+ Integer value = mockedIntPrefs.get(key);
+ return value != null ? value : defValue;
+ }
+
+ @Override
+ public long getLong(String key, long defValue) {
+ return 0;
+ }
+
+ @Override
+ public float getFloat(String key, float defValue) {
+ return 0;
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean defValue) {
+ Boolean value = mockedBooleanPrefs.get(key);
+ return value != null ? value : defValue;
+ }
+
+ @Override
+ public boolean contains(String key) {
+ return mockedStringPrefs.containsKey(key) ||
+ mockedBooleanPrefs.containsKey(key) ||
+ mockedIntPrefs.containsKey(key);
+ }
+
+ @Override
+ public Editor edit() {
+ return new Editor() {
+ private HashMap<String, String> tempStrings = new HashMap<>(mockedStringPrefs);
+ private HashMap<String, Integer> tempIntegers = new HashMap<>(mockedIntPrefs);
+ private HashMap<String, Boolean> tempBoolean = new HashMap<>(mockedBooleanPrefs);
+
+ @Override
+ public Editor putString(String key, @Nullable String value) {
+ tempStrings.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Editor putStringSet(String key, @Nullable Set<String> values) {
+ return null;
+ }
+
+ @Override
+ public Editor putInt(String key, int value) {
+ tempIntegers.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Editor putLong(String key, long value) {
+ return null;
+ }
+
+ @Override
+ public Editor putFloat(String key, float value) {
+ return null;
+ }
+
+ @Override
+ public Editor putBoolean(String key, boolean value) {
+ tempBoolean.put(key, value);
+ return this;
+ }
+
+ @Override
+ public Editor remove(String key) {
+ tempBoolean.remove(key);
+ tempStrings.remove(key);
+ tempIntegers.remove(key);
+ return this;
+ }
+
+ @Override
+ public Editor clear() {
+ tempBoolean.clear();
+ tempStrings.clear();
+ tempIntegers.clear();
+ return this;
+ }
+
+ @Override
+ public boolean commit() {
+ mockedStringPrefs = new HashMap<>(tempStrings);
+ mockedBooleanPrefs = new HashMap<>(tempBoolean);
+ mockedIntPrefs = new HashMap<>(tempIntegers);
+ return true;
+ }
+
+ @Override
+ public void apply() {
+ mockedStringPrefs = new HashMap<>(tempStrings);
+ mockedBooleanPrefs = new HashMap<>(tempBoolean);
+ mockedIntPrefs = new HashMap<>(tempIntegers);
+ }
+ };
+ }
+
+ @Override
+ public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+
+ }
+
+ @Override
+ public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
+
+ }
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java
new file mode 100644
index 00000000..f8f70eaf
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/TestSetupHelper.java
@@ -0,0 +1,434 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package se.leap.bitmaskclient.testutils;
+
+import android.content.Intent;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.ResultReceiver;
+import android.support.annotation.NonNull;
+import android.text.TextUtils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import okhttp3.OkHttpClient;
+import se.leap.bitmaskclient.ConfigHelper;
+import se.leap.bitmaskclient.OkHttpClientGenerator;
+import se.leap.bitmaskclient.R;
+import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;
+import se.leap.bitmaskclient.testutils.matchers.BundleMatcher;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.powermock.api.mockito.PowerMockito.mockStatic;
+
+/**
+ * Created by cyberta on 08.10.17.
+ */
+
+public class TestSetupHelper {
+
+
+
+ public static String getInputAsString(InputStream fileAsInputStream) throws IOException {
+ BufferedReader br = new BufferedReader(new InputStreamReader(fileAsInputStream));
+ StringBuilder sb = new StringBuilder();
+ String line = br.readLine();
+ while (line != null) {
+ sb.append(line);
+ line = br.readLine();
+ }
+
+ return sb.toString();
+ }
+
+ @NonNull
+ public static Bundle mockBundle() {
+ final Map<String, Boolean> fakeBooleanBundle = new HashMap<>();
+ final Map<String, String> fakeStringBundle = new HashMap<>();
+ final Map<String, Integer> fakeIntBundle = new HashMap<>();
+ final Map<String, Parcelable> fakeParcelableBundle = new HashMap<>();
+
+ Bundle bundle = mock(Bundle.class);
+
+ //mock String values in Bundle
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ String value = ((String) arguments[1]);
+ fakeStringBundle.put(key, value);
+ return null;
+ }
+ }).when(bundle).putString(anyString(), anyString());
+ when(bundle.getString(anyString())).thenAnswer(new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ return fakeStringBundle.get(key);
+ }
+ });
+
+ //mock Boolean values in Bundle
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ Boolean value = ((boolean) arguments[1]);
+ fakeBooleanBundle.put(key, value);
+ return null;
+ }
+ }).when(bundle).putBoolean(anyString(), anyBoolean());
+ when(bundle.getBoolean(anyString())).thenAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ return fakeBooleanBundle.get(key);
+ }
+ });
+
+ //mock Integer values in Bundle
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ Integer value = ((int) arguments[1]);
+ fakeIntBundle.put(key, value);
+ return null;
+ }
+ }).when(bundle).putInt(anyString(), anyInt());
+ when(bundle.getInt(anyString())).thenAnswer(new Answer<Integer>() {
+ @Override
+ public Integer answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ return fakeIntBundle.get(key);
+ }
+ });
+
+ //mock Parcelable values in Bundle
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ Parcelable value = ((Parcelable) arguments[1]);
+ fakeParcelableBundle.put(key, value);
+ return null;
+ }
+ }).when(bundle).putParcelable(anyString(), any(Parcelable.class));
+ when(bundle.getParcelable(anyString())).thenAnswer(new Answer<Parcelable>() {
+ @Override
+ public Parcelable answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ return fakeParcelableBundle.get(key);
+ }
+ });
+
+ //mock get
+ when(bundle.get(anyString())).thenAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ if (fakeBooleanBundle.containsKey(key)) {
+ return fakeBooleanBundle.get(key);
+ } else if (fakeIntBundle.containsKey(key)) {
+ return fakeIntBundle.get(key);
+ } else if (fakeStringBundle.containsKey(key)) {
+ return fakeStringBundle.get(key);
+ } else {
+ return fakeParcelableBundle.get(key);
+ }
+ }
+ });
+
+ //mock getKeySet
+ when(bundle.keySet()).thenAnswer(new Answer<Set<String>>() {
+ @Override
+ public Set<String> answer(InvocationOnMock invocation) throws Throwable {
+ //this whole approach as a drawback:
+ //you should not add the same keys for values of different types
+ HashSet<String> keys = new HashSet<String>();
+ keys.addAll(fakeBooleanBundle.keySet());
+ keys.addAll(fakeIntBundle.keySet());
+ keys.addAll(fakeStringBundle.keySet());
+ keys.addAll(fakeParcelableBundle.keySet());
+ return keys;
+ }
+ });
+
+ //mock containsKey
+ when(bundle.containsKey(anyString())).thenAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ String key = (String) invocation.getArguments()[0];
+ return fakeBooleanBundle.containsKey(key) ||
+ fakeStringBundle.containsKey(key) ||
+ fakeIntBundle.containsKey(key) ||
+ fakeParcelableBundle.containsKey(key);
+ }
+ });
+
+ return bundle;
+ }
+
+ public static Intent mockIntent() {
+ Intent intent = mock(Intent.class);
+ final String[] action = new String[1];
+ final Map<String, Object> fakeExtras = new HashMap<>();
+ final List<String> categories = new ArrayList<>();
+
+
+ //mock Action in intent
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ action[0] = ((String) arguments[0]);
+ return null;
+ }
+ }).when(intent).setAction(anyString());
+ when(intent.getAction()).thenAnswer(new Answer<String>() {
+ @Override
+ public String answer(InvocationOnMock invocation) throws Throwable {
+ return action[0];
+ }
+ });
+
+ //mock Bundle in intent extras
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ Bundle value = ((Bundle) arguments[1]);
+ fakeExtras.put(key, value);
+ return null;
+ }
+ }).when(intent).putExtra(anyString(), any(Bundle.class));
+ when(intent.getBundleExtra(anyString())).thenAnswer(new Answer<Bundle>() {
+ @Override
+ public Bundle answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ return (Bundle) fakeExtras.get(key);
+ }
+ });
+
+ //mock Parcelable in intent extras
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ Parcelable value = ((Parcelable) arguments[1]);
+ fakeExtras.put(key, value);
+ return null;
+ }
+ }).when(intent).putExtra(anyString(), any(Parcelable.class));
+ when(intent.getParcelableExtra(anyString())).thenAnswer(new Answer<Parcelable>() {
+ @Override
+ public Parcelable answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ String key = ((String) arguments[0]);
+ return (Parcelable) fakeExtras.get(key);
+ }
+ });
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ categories.add(((String) arguments[0]));
+ return null;
+ }
+ }).when(intent).addCategory(anyString());
+
+ when(intent.getCategories()).thenAnswer(new Answer<Set<String>>() {
+ @Override
+ public Set<String> answer(InvocationOnMock invocation) throws Throwable {
+ return new HashSet<>(categories);
+ }
+ });
+
+ return intent;
+ }
+
+ public static void mockTextUtils() {
+ mockStatic(TextUtils.class);
+
+ when(TextUtils.equals(any(CharSequence.class), any(CharSequence.class))).thenAnswer(new Answer<Object>() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ CharSequence a = (CharSequence) invocation.getArguments()[0];
+ CharSequence b = (CharSequence) invocation.getArguments()[1];
+ if (a == b) return true;
+ int length;
+ if (a != null && b != null && (length = a.length()) == b.length()) {
+ if (a instanceof String && b instanceof String) {
+ return a.equals(b);
+ } else {
+ for (int i = 0; i < length; i++) {
+ if (a.charAt(i) != b.charAt(i)) return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ });
+
+ when(TextUtils.isEmpty(any(CharSequence.class))).thenAnswer(new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ CharSequence param = (CharSequence) invocation.getArguments()[0];
+ return param == null || param.length() == 0;
+ }
+ });
+ }
+
+
+ public static ResultReceiver mockResultReceiver(final int expectedResultCode, final Bundle expectedBundle) {
+ ResultReceiver resultReceiver = mock(ResultReceiver.class);
+
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ Object[] arguments = invocation.getArguments();
+ int resultCode = (int) arguments[0];
+ Bundle bundle = (Bundle) arguments[1];
+ Set<String> keys = expectedBundle.keySet();
+ Iterator<String> iterator = keys.iterator();
+ HashMap<String, Integer> expectedIntegers = new HashMap<>();
+ HashMap<String, String> expectedStrings = new HashMap<>();
+ HashMap<String, Boolean> expectedBooleans = new HashMap<>();
+ HashMap<String, Parcelable> expectedParcelables = new HashMap<>();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ Object value = expectedBundle.get(key);
+
+ if (value instanceof Boolean) {
+ expectedBooleans.put(key, (Boolean) value);
+ } else if (value instanceof Integer) {
+ expectedIntegers.put(key, (Integer) value);
+ } else if (value instanceof String) {
+ expectedStrings.put(key, (String) value);
+ } else if (value instanceof Parcelable) {
+ expectedParcelables.put(key, (Parcelable) value);
+ }
+ }
+ assertThat("expected bundle: ", bundle, new BundleMatcher(expectedIntegers, expectedStrings, expectedBooleans, expectedParcelables));
+ assertEquals("expected resultCode: ", expectedResultCode, resultCode);
+ return null;
+ }
+ }).when(resultReceiver).send(anyInt(), any(Bundle.class));
+ return resultReceiver;
+ }
+
+ public static void mockFingerprintForCertificate(String mockedFingerprint) throws CertificateEncodingException, NoSuchAlgorithmException {
+ mockStatic(ConfigHelper.class);
+ when(ConfigHelper.getFingerprintFromCertificate(any(X509Certificate.class), anyString())).thenReturn(mockedFingerprint);
+ when(ConfigHelper.checkErroneousDownload(anyString())).thenCallRealMethod();
+ when(ConfigHelper.parseX509CertificateFromString(anyString())).thenCallRealMethod();
+ }
+
+ public static void mockProviderApiConnector(final BackendMockProvider.TestBackendErrorCase errorCase) throws IOException {
+ BackendMockProvider.provideBackendResponsesFor(errorCase);
+ }
+
+ public static OkHttpClientGenerator mockClientGenerator() {
+ OkHttpClientGenerator mockClientGenerator = mock(OkHttpClientGenerator.class);
+ OkHttpClient mockedOkHttpClient = mock(OkHttpClient.class);
+ when(mockClientGenerator.initCommercialCAHttpClient(any(JSONObject.class))).thenReturn(mockedOkHttpClient);
+ when(mockClientGenerator.initSelfSignedCAHttpClient(any(JSONObject.class))).thenReturn(mockedOkHttpClient);
+ when(mockClientGenerator.initSelfSignedCAHttpClient(any(JSONObject.class), anyString())).thenReturn(mockedOkHttpClient);
+ return mockClientGenerator;
+ }
+
+ public static Resources mockResources(InputStream inputStream) throws IOException, JSONException {
+ Resources mockedResources = mock(Resources.class, RETURNS_DEEP_STUBS);
+ JSONObject errorMessages = new JSONObject(getInputAsString(inputStream));
+
+
+ when(mockedResources.getString(R.string.warning_corrupted_provider_details)).
+ thenReturn(errorMessages.getString("warning_corrupted_provider_details"));
+ when(mockedResources.getString(R.string.server_unreachable_message)).
+ thenReturn(errorMessages.getString("server_unreachable_message"));
+ when(mockedResources.getString(R.string.error_security_pinnedcertificate)).
+ thenReturn(errorMessages.getString("error.security.pinnedcertificate"));
+ when(mockedResources.getString(R.string.malformed_url)).
+ thenReturn(errorMessages.getString("malformed_url"));
+ when(mockedResources.getString(R.string.certificate_error)).
+ thenReturn(errorMessages.getString("certificate_error"));
+ when(mockedResources.getString(R.string.error_srp_math_error_user_message)).
+ thenReturn(errorMessages.getString("error_srp_math_error_user_message"));
+ when(mockedResources.getString(R.string.error_bad_user_password_user_message)).
+ thenReturn(errorMessages.getString("error_bad_user_password_user_message"));
+ when(mockedResources.getString(R.string.error_not_valid_password_user_message)).
+ thenReturn(errorMessages.getString("error_not_valid_password_user_message"));
+ when(mockedResources.getString(R.string.error_client_http_user_message)).
+ thenReturn(errorMessages.getString("error_client_http_user_message"));
+ when(mockedResources.getString(R.string.error_io_exception_user_message)).
+ thenReturn(errorMessages.getString("error_io_exception_user_message"));
+ when(mockedResources.getString(R.string.error_json_exception_user_message)).
+ thenReturn(errorMessages.getString("error_json_exception_user_message"));
+ when(mockedResources.getString(R.string.error_no_such_algorithm_exception_user_message)).
+ thenReturn(errorMessages.getString("error_no_such_algorithm_exception_user_message"));
+ when(mockedResources.getString(R.string.warning_corrupted_provider_details)).
+ thenReturn(errorMessages.getString("warning_corrupted_provider_details"));
+ when(mockedResources.getString(R.string.warning_corrupted_provider_cert)).
+ thenReturn(errorMessages.getString("warning_corrupted_provider_cert"));
+ when(mockedResources.getString(R.string.warning_expired_provider_cert)).
+ thenReturn(errorMessages.getString("warning_expired_provider_cert"));
+ return mockedResources;
+ }
+
+}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java b/app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java
new file mode 100644
index 00000000..d2d2a102
--- /dev/null
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/matchers/BundleMatcher.java
@@ -0,0 +1,208 @@
+/**
+ * Copyright (c) 2018 LEAP Encryption Access Project and contributers
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+package se.leap.bitmaskclient.testutils.matchers;
+
+import android.os.Bundle;
+import android.os.Parcelable;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Created by cyberta on 09.01.18.
+ */
+
+public class BundleMatcher extends BaseMatcher<Bundle> {
+
+ private final HashMap<String, Integer> expectedIntegers;
+ private final HashMap<String, String> expectedStrings;
+ private final HashMap<String, Boolean> expectedBooleans;
+ private final HashMap<String, Parcelable> expectedParcelables;
+ private HashMap<String, Integer> unfoundExpectedInteger = new HashMap<>();
+ private HashMap<String, Boolean> unfoundExpectedBoolean = new HashMap<>();
+ private HashMap<String, String> unfoundExpectedString = new HashMap<>();
+ private HashMap<String, Parcelable> unfoundExpectedParcelable = new HashMap<>();
+ private HashMap<String, Object> unexpectedAdditionalObjects = new HashMap<>();
+
+ public BundleMatcher(HashMap<String, Integer> expectedIntegers, HashMap<String, String> expectedStrings, HashMap<String, Boolean> expectedBooleans, HashMap<String, Parcelable> expectedParcelables) {
+ this.expectedBooleans = expectedBooleans;
+ this.expectedIntegers = expectedIntegers;
+ this.expectedStrings = expectedStrings;
+ this.expectedParcelables = expectedParcelables;
+ }
+
+ @Override
+ public boolean matches(Object item) {
+ if (item instanceof Bundle) {
+ Bundle actualBundle = (Bundle) item;
+ return checkActualBundleHasAllExpectedBooleanValues(actualBundle) &&
+ checkActualBundleHasAllExpectedStringValues(actualBundle) &&
+ checkActualBundleHasAllExpectedIntValues(actualBundle) &&
+ checkActualBundleHasAllExpectedParcelableValues(actualBundle) &&
+ checkUnexpectedAdditionalValuesIn(actualBundle);
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Bundle didn't match expectation!");
+
+ if (!unfoundExpectedInteger.isEmpty()) {
+ Iterator<String> iterator = unfoundExpectedInteger.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ if (unfoundExpectedInteger.get(key) == null) {
+ description.appendText("\n unfound Integer in actual Bundle: ").appendValue(iterator.next());
+ } else {
+ description.appendText("\n expected Integer for key " + key).appendValue(expectedIntegers.get(key)).
+ appendText("\n found Integer was: ").appendValue(unfoundExpectedInteger.get(key));
+ }
+ }
+ }
+ if (!unfoundExpectedBoolean.isEmpty()) {
+ Iterator<String> iterator = unfoundExpectedBoolean.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ if (unfoundExpectedBoolean.get(key) == null) {
+ description.appendText("\n unfound Boolean in actual Bundle: ").appendValue(iterator.next());
+ } else {
+ description.appendText("\n expected Boolean for key " + key).appendValue(expectedBooleans.get(key)).
+ appendText("\n found Boolean was: ").appendValue(unfoundExpectedBoolean.get(key));
+ }
+ }
+ }
+ if (!unfoundExpectedString.isEmpty()) {
+ Iterator<String> iterator = unfoundExpectedString.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ if (unfoundExpectedString.get(key) == null) {
+ description.appendText("\n unfound String in actual Bundle: ").appendValue(iterator.next());
+ } else {
+ description.appendText("\n expected String for key " + key).appendValue(expectedStrings.get(key)).
+ appendText("\n found String was: ").appendValue(unfoundExpectedString.get(key));
+ }
+ }
+ }
+ if (!unfoundExpectedParcelable.isEmpty()) {
+ Iterator<String> iterator = unfoundExpectedInteger.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ if (unfoundExpectedParcelable.get(key) == null) {
+ description.appendText("\n unfound Parcelable in actual Bundle: ").appendValue(iterator.next());
+ } else {
+ description.appendText("\n expected Parcelable or key " + key).appendValue(expectedParcelables.get(key)).
+ appendText("\n found Parcelable was: ").appendValue(unfoundExpectedParcelable.get(key));
+ }
+ }
+ }
+
+ if (!unexpectedAdditionalObjects.isEmpty()) {
+ Iterator<String> iterator = unexpectedAdditionalObjects.keySet().iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next();
+ Object value = unexpectedAdditionalObjects.get(key);
+ if (value instanceof String) {
+ description.appendText("\n unexpected String found in actual Bundle: ").appendValue(key).appendText(", ").appendValue(value);
+ } else if (value instanceof Boolean) {
+ description.appendText("\n unexpected Boolean found in actual Bundle: ").appendValue(key).appendText(", ").appendValue(value);
+ } else if (value instanceof Integer) {
+ description.appendText("\n unexpected Integer found in actual Bundle: ").appendValue(key).appendText(", ").appendValue(value);
+ } else if (value instanceof Parcelable) {
+ description.appendText("\n unexpected Parcelable found in actual Bundle: ").appendValue(key).appendText(", ").appendValue(value);
+ } else {
+ description.appendText("\n unexpected Object found in actual Bundle: ").appendValue(key).appendText(", ").appendValue(value);
+ }
+ }
+ }
+ }
+
+ private boolean checkActualBundleHasAllExpectedBooleanValues(Bundle actualBundle) {
+ Set<String> booleanKeys = expectedBooleans.keySet();
+ for (String key : booleanKeys) {
+ Object valueObject = actualBundle.get(key);
+ if (valueObject == null ||
+ !(valueObject instanceof Boolean) ||
+ valueObject != expectedBooleans.get(key)) {
+ unfoundExpectedBoolean.put(key, (Boolean) valueObject);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkActualBundleHasAllExpectedStringValues(Bundle actualBundle) {
+ Set<String> stringKeys = expectedStrings.keySet();
+ for (String key : stringKeys) {
+ Object valueObject = actualBundle.get(key);
+ if (valueObject == null ||
+ !(valueObject instanceof String) ||
+ !valueObject.equals(expectedStrings.get(key))) {
+ unfoundExpectedString.put(key, (String) valueObject);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkActualBundleHasAllExpectedIntValues(Bundle actualBundle) {
+ Set<String> stringKeys = expectedIntegers.keySet();
+ for (String key : stringKeys) {
+ Object valueObject = actualBundle.get(key);
+ if (valueObject == null ||
+ !(valueObject instanceof Integer) ||
+ ((Integer) valueObject).compareTo(expectedIntegers.get(key)) != 0) {
+ unfoundExpectedInteger.put(key, (Integer) valueObject);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkActualBundleHasAllExpectedParcelableValues(Bundle actualBundle) {
+ Set<String> stringKeys = expectedParcelables.keySet();
+ for (String key : stringKeys) {
+ Object valueObject = actualBundle.get(key);
+ if (valueObject == null ||
+ !(valueObject instanceof Parcelable) ||
+ !valueObject.equals(expectedParcelables.get(key))) {
+ unfoundExpectedParcelable.put(key, (Parcelable) valueObject);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkUnexpectedAdditionalValuesIn(Bundle actualBundle) {
+ Set<String> keys = actualBundle.keySet();
+
+ for (String key : keys) {
+ if (!expectedStrings.containsKey(key) &&
+ !expectedIntegers.containsKey(key) &&
+ !expectedBooleans.containsKey(key) &&
+ !expectedParcelables.containsKey(key)
+ ) {
+ unexpectedAdditionalObjects.put(key, actualBundle.getString(key));
+ }
+ }
+ return unexpectedAdditionalObjects.isEmpty();
+ }
+}