summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2021-11-07 05:20:25 +0100
committercyBerta <cyberta@riseup.net>2021-11-07 05:20:25 +0100
commit7bde8bbb733dfcba7baace5c2261f67da8e0d3d0 (patch)
treea350614b04c41ff0a38fc13251f4ccc935cad6d9 /app
parent7ece2b7cf81ac1e69003f288fc15f7d56ab9ca25 (diff)
Add provider setup unit tests wrt. Tor handling
Diffstat (limited to 'app')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java6
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java42
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java184
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java6
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/TorFallbackBackendResponse.java86
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java85
-rw-r--r--app/src/test/resources/error_messages.json3
7 files changed, 369 insertions, 43 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
index 3be4ce12..fae7d063 100644
--- a/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
+++ b/app/src/main/java/se/leap/bitmaskclient/tor/TorStatusObservable.java
@@ -83,8 +83,9 @@ public class TorStatusObservable extends Observable {
* @param timeout Timout in seconds
* @throws InterruptedException if thread was interrupted while waiting
* @throws TimeoutException thrown if timeout was reached
+ * @return true return value only needed to mock this method call
*/
- public static void waitUntil(StatusCondition condition, int timeout) throws InterruptedException, TimeoutException {
+ public static boolean waitUntil(StatusCondition condition, int timeout) throws InterruptedException, TimeoutException {
CountDownLatch countDownLatch = new CountDownLatch(1);
final AtomicBoolean conditionMet = new AtomicBoolean(false);
Observer observer = (o, arg) -> {
@@ -95,7 +96,7 @@ public class TorStatusObservable extends Observable {
};
if (condition.met()) {
// no need to wait
- return;
+ return true;
}
getInstance().addObserver(observer);
countDownLatch.await(timeout, TimeUnit.SECONDS);
@@ -103,6 +104,7 @@ public class TorStatusObservable extends Observable {
if (!conditionMet.get()) {
throw new TimeoutException("Status condition not met within " + timeout + "s.");
}
+ return true;
}
public static void logSnowflakeMessage(Context context, String message) {
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 0c122c89..08591b6d 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/GatewaysManagerTest.java
@@ -81,7 +81,7 @@ public class GatewaysManagerTest {
@Test
public void testGatewayManagerFromCurrentProvider_noProvider_noGateways() {
- MockHelper.mockProviderObserver(null);
+ MockHelper.mockProviderObservable(null);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
assertEquals(0, gatewaysManager.size());
}
@@ -89,7 +89,7 @@ public class GatewaysManagerTest {
@Test
public void testGatewayManagerFromCurrentProvider_misconfiguredProvider_noGateways() throws IOException, NullPointerException {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_misconfigured_gateway.json", null);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
assertEquals(0, gatewaysManager.size());
}
@@ -97,7 +97,7 @@ public class GatewaysManagerTest {
@Test
public void testGatewayManagerFromCurrentProvider_threeGateways() {
Provider provider = getProvider(null, null, null, null,null, null, "ptdemo_three_mixed_gateways.json", null);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
assertEquals(3, gatewaysManager.size());
}
@@ -107,7 +107,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null);
JSONObject eipServiceJson = provider.getEipServiceJson();
JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3);
@@ -122,7 +122,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null);
JSONObject eipServiceJson = provider.getEipServiceJson();
JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3);
@@ -137,7 +137,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json");
JSONObject eipServiceJson = provider.getEipServiceJson();
JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3);
@@ -152,7 +152,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json");
JSONObject eipServiceJson = provider.getEipServiceJson();
JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3);
@@ -167,7 +167,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null);
JSONObject eipServiceJson = provider.getEipServiceJson();
JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(0);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3);
@@ -182,7 +182,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", null);
JSONObject eipServiceJson = provider.getEipServiceJson();
JSONObject gateway1 = eipServiceJson.getJSONArray(GATEWAYS).getJSONObject(1);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
VpnConfigGenerator configGenerator = new VpnConfigGenerator(provider.getDefinition(), secrets, gateway1, 3);
@@ -196,7 +196,7 @@ public class GatewaysManagerTest {
public void TestSelectN_selectFirstObfs4Connection_returnThirdGateway() {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_two_openvpn_one_pt_gateways.json", null);
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
@@ -208,7 +208,7 @@ public class GatewaysManagerTest {
public void testSelectN_selectFromPresortedGateways_returnsGatewaysInPresortedOrder() {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -223,7 +223,7 @@ public class GatewaysManagerTest {
public void testSelectN_selectObfs4FromPresortedGateways_returnsObfs4GatewaysInPresortedOrder() {
Provider provider = getProvider(null, null, null, null, null, null, "ptdemo_three_mixed_gateways.json", "ptdemo_three_mixed_gateways.geoip.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true);
@@ -239,7 +239,7 @@ public class GatewaysManagerTest {
public void testSelectN_selectFromCity_returnsGatewaysInPresortedOrder() {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -255,7 +255,7 @@ public class GatewaysManagerTest {
public void testSelectN_selectFromCityWithGeoIpServiceV1_returnsGatewaysInPresortedOrder() {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v1.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -272,7 +272,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", null);
provider.setGeoIpJson(new JSONObject());
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -289,7 +289,7 @@ public class GatewaysManagerTest {
public void testSelectN_selectNAndCity_returnsGatewaysInPresortedOrder() {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -304,7 +304,7 @@ public class GatewaysManagerTest {
public void testSelectN_selectNAndCityWithGeoIpServiceV1_returnsGatewaysInPresortedOrder() {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v1.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -320,7 +320,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", null);
provider.setGeoIpJson(new JSONObject());
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -337,7 +337,7 @@ public class GatewaysManagerTest {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4.json");
provider.setGeoIpJson(new JSONObject());
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
//use openvpn, not pluggable transports
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
@@ -349,7 +349,7 @@ public class GatewaysManagerTest {
public void testGetLocations_openvpn() {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(false);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
@@ -370,7 +370,7 @@ public class GatewaysManagerTest {
public void testGetLocations_obfs4() {
Provider provider = getProvider(null, null, null, null, null, null, "v4/riseup_eipservice_for_geoip_v4.json", "v4/riseup_geoip_v4.json");
- MockHelper.mockProviderObserver(provider);
+ MockHelper.mockProviderObservable(provider);
mockStatic(PreferenceHelper.class);
when(PreferenceHelper.getUseBridges(any(Context.class))).thenReturn(true);
GatewaysManager gatewaysManager = new GatewaysManager(mockContext);
diff --git a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
index cd8e34cb..5ecde5d4 100644
--- a/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
+++ b/app/src/test/java/se/leap/bitmaskclient/eip/ProviderApiManagerTest.java
@@ -24,6 +24,8 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.text.TextUtils;
+import androidx.annotation.Nullable;
+
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;
@@ -38,29 +40,36 @@ import org.powermock.modules.junit4.PowerMockRunner;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateEncodingException;
+import java.util.concurrent.TimeoutException;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.base.models.Provider;
+import se.leap.bitmaskclient.base.utils.ConfigHelper;
+import se.leap.bitmaskclient.base.utils.PreferenceHelper;
import se.leap.bitmaskclient.providersetup.ProviderAPI;
import se.leap.bitmaskclient.providersetup.ProviderApiConnector;
import se.leap.bitmaskclient.providersetup.ProviderApiManager;
import se.leap.bitmaskclient.providersetup.ProviderApiManagerBase;
import se.leap.bitmaskclient.testutils.MockSharedPreferences;
-import se.leap.bitmaskclient.base.utils.ConfigHelper;
-import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
+import static org.junit.Assert.assertEquals;
import static se.leap.bitmaskclient.base.models.Constants.BROADCAST_RESULT_KEY;
import static se.leap.bitmaskclient.base.models.Constants.EIP_ACTION_START;
import static se.leap.bitmaskclient.base.models.Constants.PROVIDER_KEY;
+import static se.leap.bitmaskclient.base.models.Constants.USE_BRIDGES;
+import static se.leap.bitmaskclient.base.models.Constants.USE_TOR;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.CORRECTLY_DOWNLOADED_GEOIP_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.INCORRECTLY_DOWNLOADED_GEOIP_JSON;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.PARAMETERS;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_NOK;
import static se.leap.bitmaskclient.providersetup.ProviderAPI.PROVIDER_OK;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.TOR_TIMEOUT;
import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_FETCH_EIP_SERVICE_CERTIFICATE_INVALID;
import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_MICONFIGURED_PROVIDER;
import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_CASE_UPDATED_CERTIFICATE;
+import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_DNS_RESUOLUTION_TOR_FALLBACK;
import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.ERROR_GEOIP_SERVICE_IS_DOWN;
import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR;
import static se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider.TestBackendErrorCase.NO_ERROR_API_V4;
@@ -74,6 +83,7 @@ import static se.leap.bitmaskclient.testutils.MockHelper.mockProviderApiConnecto
import static se.leap.bitmaskclient.testutils.MockHelper.mockResources;
import static se.leap.bitmaskclient.testutils.MockHelper.mockResultReceiver;
import static se.leap.bitmaskclient.testutils.MockHelper.mockTextUtils;
+import static se.leap.bitmaskclient.testutils.MockHelper.mockTorStatusObservable;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.getConfiguredProvider;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.getConfiguredProviderAPIv4;
import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
@@ -85,7 +95,7 @@ import static se.leap.bitmaskclient.testutils.TestSetupHelper.getProvider;
*/
@RunWith(PowerMockRunner.class)
-@PrepareForTest({ProviderApiManager.class, TextUtils.class, ConfigHelper.class, ProviderApiConnector.class, PreferenceHelper.class})
+@PrepareForTest({ProviderApiManager.class, TextUtils.class, ConfigHelper.class, ProviderApiConnector.class, PreferenceHelper.class, TorStatusObservable.class})
public class ProviderApiManagerTest {
private SharedPreferences mockPreferences;
@@ -96,24 +106,35 @@ public class ProviderApiManagerTest {
private ProviderApiManager providerApiManager;
- class TestProviderApiServiceCallback implements ProviderApiManagerBase.ProviderApiServiceCallback {
-
- //Intent expectedIntent;
- TestProviderApiServiceCallback(/*Intent expectedIntent*/) {
- //this.expectedIntent = expectedIntent;
+ static class TestProviderApiServiceCallback implements ProviderApiManagerBase.ProviderApiServiceCallback {
+ Throwable startTorServiceException;
+ boolean isConnectedToWifi;
+ TestProviderApiServiceCallback() {
+ new TestProviderApiServiceCallback(null/*, 0*/, true);
+ }
+ TestProviderApiServiceCallback(@Nullable Throwable startTorServiceException/*, int torHttpTunnelPort*/, boolean isConnectedToWifi) {
+ this.startTorServiceException = startTorServiceException;
+ this.isConnectedToWifi = isConnectedToWifi;
}
@Override
public void broadcastEvent(Intent intent) {
- //assertEquals("expected intent: ", expectedIntent, intent);
}
@Override
public void startTorService() throws InterruptedException, IllegalStateException {
+ if (startTorServiceException != null) {
+ if (startTorServiceException instanceof InterruptedException) {
+ throw (InterruptedException) startTorServiceException;
+ }
+ if (startTorServiceException instanceof IllegalStateException) {
+ throw (IllegalStateException) startTorServiceException;
+ }
+ }
}
@Override
- public void stopTorService() throws IllegalStateException {
+ public void stopTorService() {
}
@Override
@@ -123,7 +144,7 @@ public class ProviderApiManagerTest {
@Override
public boolean isConnectedToWifi() {
- return true;
+ return this.isConnectedToWifi;
}
}
@@ -600,4 +621,145 @@ public class ProviderApiManagerTest {
providerApiManager.handleIntent(providerApiCommand);
}
+ @Test
+ public void test_handleIntentSetupProvider_TorFallback_SecondTryHappyPath() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, TimeoutException, InterruptedException {
+ Provider provider = getConfiguredProviderAPIv4();
+
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_DNS_RESUOLUTION_TOR_FALLBACK);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback(null, true));
+
+ Intent providerApiCommand = mockIntent();
+ providerApiCommand.putExtra(PROVIDER_KEY, provider);
+ providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER);
+ providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK));
+
+ mockTorStatusObservable(null);
+
+ providerApiManager.handleIntent(providerApiCommand);
+ assertEquals(8118, TorStatusObservable.getProxyPort());
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_TorFallback_SecondTryFailedNoWifi() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, TimeoutException, InterruptedException {
+ Provider provider = getConfiguredProviderAPIv4();
+
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_DNS_RESUOLUTION_TOR_FALLBACK);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback(null, false));
+
+ Intent providerApiCommand = mockIntent();
+ providerApiCommand.putExtra(PROVIDER_KEY, provider);
+ providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER);
+ providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK));
+
+ mockTorStatusObservable(null);
+
+ providerApiManager.handleIntent(providerApiCommand);
+ assertEquals(-1, TorStatusObservable.getProxyPort());
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_TorFallbackStartServiceException_SecondTryFailed() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, TimeoutException, InterruptedException {
+ Provider provider = getConfiguredProviderAPIv4();
+
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_DNS_RESUOLUTION_TOR_FALLBACK);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback(new IllegalStateException("Tor service start not failed."), true));
+
+ Intent providerApiCommand = mockIntent();
+ providerApiCommand.putExtra(PROVIDER_KEY, provider);
+ providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER);
+ providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK));
+
+ mockTorStatusObservable(null);
+
+ providerApiManager.handleIntent(providerApiCommand);
+ assertEquals(-1, TorStatusObservable.getProxyPort());
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_TorFallbackTimeoutException_SecondTryFailed() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, TimeoutException, InterruptedException {
+ Provider provider = getConfiguredProviderAPIv4();
+
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(ERROR_DNS_RESUOLUTION_TOR_FALLBACK);
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback(null, true));
+
+ Intent providerApiCommand = mockIntent();
+ providerApiCommand.putExtra(PROVIDER_KEY, provider);
+ providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER);
+ providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_NOK));
+
+ mockTorStatusObservable(new TimeoutException("Tor took too long to start."));
+
+ providerApiManager.handleIntent(providerApiCommand);
+ assertEquals(-1, TorStatusObservable.getProxyPort());
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_TorBridgesPreferenceEnabled_Success() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, TimeoutException, InterruptedException {
+ Provider provider = getConfiguredProviderAPIv4();
+
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(NO_ERROR_API_V4);
+
+ mockPreferences.edit().putBoolean(USE_BRIDGES, true).putBoolean(USE_TOR, true).commit();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback(null, true));
+
+ Intent providerApiCommand = mockIntent();
+ providerApiCommand.putExtra(PROVIDER_KEY, provider);
+ providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER);
+ providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK));
+
+ mockTorStatusObservable(null);
+
+ providerApiManager.handleIntent(providerApiCommand);
+ assertEquals(8118, TorStatusObservable.getProxyPort());
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_TorBridgesDisabled_TorNotStarted() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, TimeoutException, InterruptedException {
+ Provider provider = getConfiguredProviderAPIv4();
+
+ mockFingerprintForCertificate(" a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494");
+ mockProviderApiConnector(NO_ERROR_API_V4);
+
+ mockPreferences.edit().putBoolean(USE_BRIDGES, false).putBoolean(USE_TOR, false).commit();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback(null, true));
+
+ Intent providerApiCommand = mockIntent();
+ providerApiCommand.putExtra(PROVIDER_KEY, provider);
+ providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER);
+ providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(PROVIDER_OK));
+
+ mockTorStatusObservable(new TimeoutException("This timeout exception is never thrown"));
+
+ providerApiManager.handleIntent(providerApiCommand);
+ assertEquals(-1, TorStatusObservable.getProxyPort());
+ }
+
+ @Test
+ public void test_handleIntentSetupProvider_TorBridgesPreferencesEnabledTimeout_TimeoutError() throws IOException, CertificateEncodingException, NoSuchAlgorithmException, TimeoutException, InterruptedException {
+ Provider provider = getConfiguredProviderAPIv4();
+
+ mockPreferences.edit().putBoolean(USE_BRIDGES, true).putBoolean(USE_TOR, true).commit();
+ providerApiManager = new ProviderApiManager(mockPreferences, mockResources, mockClientGenerator(), new TestProviderApiServiceCallback(null, true));
+
+ Bundle expectedResult = mockBundle();
+ expectedResult.putBoolean(BROADCAST_RESULT_KEY, false);
+ expectedResult.putString(ERRORS, "{\"errorId\":\"ERROR_TOR_TIMEOUT\",\"initalAction\":\"setUpProvider\",\"errors\":\"Starting bridges failed. Do you want to retry or continue with an unobfuscated secure connection to configure Bitmask?\"}");
+ expectedResult.putParcelable(PROVIDER_KEY, provider);
+
+ Intent providerApiCommand = mockIntent();
+ providerApiCommand.putExtra(PROVIDER_KEY, provider);
+ providerApiCommand.setAction(ProviderAPI.SET_UP_PROVIDER);
+ providerApiCommand.putExtra(ProviderAPI.RECEIVER_KEY, mockResultReceiver(TOR_TIMEOUT, expectedResult));
+
+ mockTorStatusObservable(new TimeoutException("Tor took too long to start."));
+
+ providerApiManager.handleIntent(providerApiCommand);
+ assertEquals(-1, TorStatusObservable.getProxyPort());
+ }
+
}
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
index d76e4029..27401807 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/BackendMockProvider.java
@@ -46,7 +46,8 @@ public class BackendMockProvider {
ERROR_INVALID_SESSION_TOKEN,
ERROR_NO_CONNECTION,
ERROR_WRONG_SRP_CREDENTIALS,
- NO_ERROR_API_V4
+ NO_ERROR_API_V4,
+ ERROR_DNS_RESUOLUTION_TOR_FALLBACK
}
@@ -71,6 +72,9 @@ public class BackendMockProvider {
case ERROR_GEOIP_SERVICE_IS_DOWN:
new GeoIpServiceIsDownBackendResponse();
break;
+ case ERROR_DNS_RESUOLUTION_TOR_FALLBACK:
+ new TorFallbackBackendResponse();
+ break;
case ERROR_NO_RESPONSE_BODY:
break;
case ERROR_DNS_RESOLUTION_ERROR:
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/TorFallbackBackendResponse.java b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/TorFallbackBackendResponse.java
index df847404..2f7a0ddc 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/TorFallbackBackendResponse.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/BackendMockResponses/TorFallbackBackendResponse.java
@@ -1,4 +1,88 @@
package se.leap.bitmaskclient.testutils.BackendMockResponses;
-public class TorFallbackBackendResponse {
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+
+import static se.leap.bitmaskclient.testutils.TestSetupHelper.getInputAsString;
+
+public class TorFallbackBackendResponse extends BaseBackendResponse {
+ public TorFallbackBackendResponse() throws IOException {
+ super();
+ }
+ int requestAttempt = 0;
+
+ @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")) {
+ if (requestAttempt == 0) {
+ requestAttempt++;
+ throw new UnknownHostException();
+ }
+ //download provider json
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/riseup.net.json"));
+ } else if (url.contains("/ca.crt")) {
+ if (requestAttempt == 0) {
+ requestAttempt++;
+ throw new UnknownHostException("DNS blocked by censor ;)");
+ }
+ //download provider ca cert
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.net.pem"));
+ } else if (url.contains("config/eip-service.json")) {
+ if (requestAttempt == 0) {
+ requestAttempt++;
+ throw new UnknownHostException("DNS blocked by censor ;)");
+ }
+ // download provider service json containing gateways, locations and openvpn settings
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("v4/riseup.service.json"));
+ } else if (url.contains(":9001/json")) {
+ if (requestAttempt == 0) {
+ requestAttempt++;
+ throw new UnknownHostException("DNS blocked by censor ;)");
+ }
+ // download geoip json, containing a sorted list of gateways
+ return getInputAsString(getClass().getClassLoader().getResourceAsStream("riseup.geoip.json"));
+ }
+
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForCanConnect() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ if (requestAttempt == 0) {
+ requestAttempt++;
+ throw new UnknownHostException("DNS blocked by censor ;)");
+ }
+ return true;
+ }
+ };
+ }
+
+ @Override
+ public Answer<Boolean> getAnswerForDelete() {
+ return new Answer<Boolean>() {
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ if (requestAttempt == 0) {
+ requestAttempt++;
+ throw new UnknownHostException("DNS blocked by censor ;)");
+ }
+ return true;
+ }
+ };
+ }
}
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
index 2bd96551..efb49693 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
@@ -9,9 +9,11 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.ResultReceiver;
-import androidx.annotation.NonNull;
import android.text.TextUtils;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import org.json.JSONException;
import org.json.JSONObject;
import org.mockito.invocation.InvocationOnMock;
@@ -32,18 +34,22 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.Vector;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
import okhttp3.OkHttpClient;
-import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
+import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
-import se.leap.bitmaskclient.R;
-import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;
-import se.leap.bitmaskclient.testutils.matchers.BundleMatcher;
import se.leap.bitmaskclient.base.utils.ConfigHelper;
import se.leap.bitmaskclient.base.utils.FileHelper;
import se.leap.bitmaskclient.base.utils.InputStreamHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
+import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;
+import se.leap.bitmaskclient.testutils.matchers.BundleMatcher;
+import se.leap.bitmaskclient.tor.TorStatusObservable;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@@ -202,6 +208,18 @@ public class MockHelper {
}
});
+ doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ String key = (String) invocation.getArguments()[0];
+ fakeBooleanBundle.remove(key);
+ fakeIntBundle.remove(key);
+ fakeParcelableBundle.remove(key);
+ fakeStringBundle.remove(key);
+ return null;
+ }
+ }).when(bundle).remove(anyString());
+
return bundle;
}
@@ -319,6 +337,20 @@ public class MockHelper {
});
}
+ public static ResultReceiver mockResultReceiver(final int expectedResultCode) {
+ 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];
+ assertEquals("expected resultCode: ", expectedResultCode, resultCode);
+ return null;
+ }
+ }).when(resultReceiver).send(anyInt(), any(Bundle.class));
+ return resultReceiver;
+ }
+
public static ResultReceiver mockResultReceiver(final int expectedResultCode, final Bundle expectedBundle) {
ResultReceiver resultReceiver = mock(ResultReceiver.class);
@@ -423,7 +455,46 @@ public class MockHelper {
});
}
- public static void mockProviderObserver(Provider provider) {
+ public static void mockTorStatusObservable(@Nullable Throwable exception) throws TimeoutException, InterruptedException {
+ TorStatusObservable observable = TorStatusObservable.getInstance();
+ mockStatic(TorStatusObservable.class);
+ when(TorStatusObservable.getInstance()).thenAnswer((Answer<TorStatusObservable>) invocation -> observable);
+
+ when(TorStatusObservable.getBootstrapProgress()).thenReturn(0);
+ when(TorStatusObservable.getLastLogs()).thenReturn(new Vector<>());
+ when(TorStatusObservable.getLastTorLog()).thenReturn("");
+ when(TorStatusObservable.getLastSnowflakeLog()).thenReturn("");
+ AtomicBoolean waitUntilSuccess = new AtomicBoolean(false);
+ when(TorStatusObservable.getProxyPort()).thenAnswer((Answer<Integer>) invocation -> {
+ if (waitUntilSuccess.get()) {
+ return 8118;
+ }
+ return -1;
+ });
+ when(TorStatusObservable.getStatus()).thenAnswer((Answer<TorStatusObservable.TorStatus>) invocation -> {
+ if (waitUntilSuccess.get()) {
+ return TorStatusObservable.TorStatus.ON;
+ }
+ return TorStatusObservable.TorStatus.OFF;
+ });
+ when(TorStatusObservable.getSnowflakeStatus()).thenAnswer((Answer<TorStatusObservable.SnowflakeStatus>) invocation -> {
+ if (waitUntilSuccess.get()) {
+ return TorStatusObservable.SnowflakeStatus.ON;
+ }
+ return TorStatusObservable.SnowflakeStatus.OFF;
+ });
+
+ if (exception != null) {
+ when(TorStatusObservable.waitUntil(any(TorStatusObservable.StatusCondition.class), anyInt())).thenThrow(exception);
+ } else {
+ when(TorStatusObservable.waitUntil(any(TorStatusObservable.StatusCondition.class), anyInt())).thenAnswer((Answer<Boolean>) invocation -> {
+ waitUntilSuccess.set(true);
+ return true;
+ });
+ }
+ }
+
+ public static void mockProviderObservable(Provider provider) {
ProviderObservable observable = ProviderObservable.getInstance();
observable.updateProvider(provider);
mockStatic(ProviderObservable.class);
@@ -498,6 +569,8 @@ public class MockHelper {
thenReturn(String.format(errorMessages.getString("setup_error_text"), "Bitmask"));
when(mockedResources.getString(R.string.app_name)).
thenReturn("Bitmask");
+ when(mockedResources.getString(eq(R.string.error_tor_timeout), anyString())).
+ thenReturn(String.format(errorMessages.getString("error_tor_timeout"), "Bitmask"));
return mockedResources;
}
diff --git a/app/src/test/resources/error_messages.json b/app/src/test/resources/error_messages.json
index e3b92d78..c5e17683 100644
--- a/app/src/test/resources/error_messages.json
+++ b/app/src/test/resources/error_messages.json
@@ -13,5 +13,6 @@
"warning_corrupted_provider_details": "Stored provider details are corrupted. You can either update %s (recommended) or update the provider details using a commercial CA certificate.",
"warning_corrupted_provider_cert": "Stored provider certificate is invalid. You can either update %s (recommended) or update the provider certificate using a commercial CA certificate.",
"warning_expired_provider_cert": "Stored provider certificate is expired. You can either update %s (recommended) or update the provider certificate using a commercial CA certificate.",
- "setup_error_text": "There was an error configuring %s with your chosen provider."
+ "setup_error_text": "There was an error configuring %s with your chosen provider.",
+ "error_tor_timeout": "Starting bridges failed. Do you want to retry or continue with an unobfuscated secure connection to configure %s?"
} \ No newline at end of file