From f855c9ea282a5e0cab15281f2495bc0b6eb6d201 Mon Sep 17 00:00:00 2001 From: Ernir Erlingsson <ernir@ymirmobile.com> Date: Wed, 4 May 2016 21:48:13 +0200 Subject: [PATCH] Finished unit tests for SetupActivity and its Controller --- briar-android/build.gradle | 3 + .../briarproject/android/SetupActivity.java | 2 +- .../controller/SetupControllerImp.java | 8 + .../system/AndroidSeedProvider.java | 7 +- .../activity/MockedSetupActivity.java | 85 -------- .../activity/SetupActivityTest.java | 184 +++++++++++++----- .../activity/TestSetupActivity.java | 20 ++ .../controller/SetupControllerTest.java | 19 -- .../system/LinuxSeedProvider.java | 2 - 9 files changed, 176 insertions(+), 154 deletions(-) delete mode 100644 briar-android/test/java/briarproject/activity/MockedSetupActivity.java create mode 100644 briar-android/test/java/briarproject/activity/TestSetupActivity.java delete mode 100644 briar-android/test/java/briarproject/controller/SetupControllerTest.java diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 815c8de62e..51b2584e05 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -105,6 +105,9 @@ android { test.setRoot('test') test { java.srcDirs = ['test/java'] + testOptions { + unitTests.returnDefaultValues = true + } } diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java index 1b101d2e01..13bb49f4b2 100644 --- a/briar-android/src/org/briarproject/android/SetupActivity.java +++ b/briar-android/src/org/briarproject/android/SetupActivity.java @@ -36,7 +36,7 @@ public class SetupActivity extends BaseActivity implements OnClickListener, OnEditorActionListener { @Inject - SetupController setupController; + protected SetupController setupController; TextInputLayout nicknameEntryWrapper; TextInputLayout passwordEntryWrapper; diff --git a/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java b/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java index 61a933e677..22eeeb84f9 100644 --- a/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java +++ b/briar-android/src/org/briarproject/android/controller/SetupControllerImp.java @@ -84,15 +84,23 @@ public class SetupControllerImp implements SetupController { @Override public void createIdentity(final String nickname, final String password, final ResultHandler<Long> resultHandler) { + LOG.info("createIdentity called"); cryptoExecutor.execute(new Runnable() { public void run() { + LOG.info("createIdentity running"); SecretKey key = crypto.generateSecretKey(); + LOG.info("createIdentity 1"); databaseConfig.setEncryptionKey(key); + LOG.info("createIdentity 2"); String hex = encryptDatabaseKey(key, password); + LOG.info("createIdentity 3"); storeEncryptedDatabaseKey(hex); + LOG.info("createIdentity 4"); final LocalAuthor localAuthor = createLocalAuthor(nickname); + LOG.info("createIdentity 5"); long handle = referenceManager.putReference(localAuthor, LocalAuthor.class); + LOG.info("createIdentity 6"); resultHandler.onResult(handle); } }); diff --git a/briar-android/src/org/briarproject/system/AndroidSeedProvider.java b/briar-android/src/org/briarproject/system/AndroidSeedProvider.java index 749d1c68bc..1b51be767d 100644 --- a/briar-android/src/org/briarproject/system/AndroidSeedProvider.java +++ b/briar-android/src/org/briarproject/system/AndroidSeedProvider.java @@ -34,7 +34,10 @@ class AndroidSeedProvider extends LinuxSeedProvider { if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT); if (Build.SERIAL != null) out.writeUTF(Build.SERIAL); ContentResolver contentResolver = appContext.getContentResolver(); - out.writeUTF(Settings.Secure.getString(contentResolver, ANDROID_ID)); - super.writeToEntropyPool(out); + String str = Settings.Secure.getString(contentResolver, ANDROID_ID); + if (str != null) { + out.writeUTF(str); + super.writeToEntropyPool(out); + } } } diff --git a/briar-android/test/java/briarproject/activity/MockedSetupActivity.java b/briar-android/test/java/briarproject/activity/MockedSetupActivity.java deleted file mode 100644 index 77a830f32a..0000000000 --- a/briar-android/test/java/briarproject/activity/MockedSetupActivity.java +++ /dev/null @@ -1,85 +0,0 @@ -package briarproject.activity; - -import org.briarproject.android.ActivityModule; -import org.briarproject.android.SetupActivity; -import org.briarproject.android.controller.SetupController; -import org.briarproject.android.controller.SetupControllerImp; -import org.briarproject.android.controller.handler.ResultHandler; -import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.logging.Logger; - -import static org.briarproject.api.crypto.PasswordStrengthEstimator.NONE; -import static org.briarproject.api.crypto.PasswordStrengthEstimator.QUITE_STRONG; -import static org.briarproject.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; -import static org.briarproject.api.crypto.PasswordStrengthEstimator.STRONG; -import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.anyString; - -public class MockedSetupActivity extends SetupActivity { - - private static final Logger LOG = - Logger.getLogger(MockedSetupActivity.class.getName()); - - final static String STRONG_PASS = "strong"; - final static String QSTRONG_PASS = "qstrong"; - final static String QWEAK_PASS = "qweak"; - final static String WEAK_PASS = "weak"; - final static String NO_PASS = "none"; - - @Override - protected ActivityModule getActivityModule() { - return new ActivityModule(this) { - - @Override - protected SetupController provideSetupController( - SetupControllerImp setupControllerImp) { - SetupController setupController = - Mockito.mock(SetupControllerImp.class); - - Mockito.doAnswer(new Answer<Void>() { - @Override - public Void answer(InvocationOnMock invocation) - throws Throwable { - ((ResultHandler<Long>) invocation.getArguments()[2]) - .onResult(1L); - return null; - } - }).when(setupController) - .createIdentity(anyString(), anyString(), - (ResultHandler<Long>) any()); - Mockito.when( - setupController - .estimatePasswordStrength(anyString())) - .thenAnswer(new Answer<Float>() { - @Override - public Float answer( - InvocationOnMock invocation) - throws Throwable { - String p = (String) invocation - .getArguments()[0]; - LOG.info("p = " + p); - if (p.equals(STRONG_PASS)) { - return STRONG; - } else if (p.equals(QSTRONG_PASS)) { - return QUITE_STRONG; - } else if (p.equals(QWEAK_PASS)) { - return QUITE_WEAK; - } else if (p.equals(WEAK_PASS)) { - return WEAK; - } else if (p.equals(NO_PASS)) { - return NONE; - } else { - return STRONG; - } - } - }); - - return setupController; - } - }; - } -} diff --git a/briar-android/test/java/briarproject/activity/SetupActivityTest.java b/briar-android/test/java/briarproject/activity/SetupActivityTest.java index 1317c61809..586cec9895 100644 --- a/briar-android/test/java/briarproject/activity/SetupActivityTest.java +++ b/briar-android/test/java/briarproject/activity/SetupActivityTest.java @@ -1,6 +1,8 @@ package briarproject.activity; +import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.support.design.widget.TextInputLayout; import android.widget.Button; import android.widget.EditText; @@ -10,47 +12,45 @@ import com.google.common.base.Strings; import org.briarproject.BuildConfig; import org.briarproject.R; import org.briarproject.android.NavDrawerActivity; -import org.briarproject.android.SetupActivity; +import org.briarproject.android.controller.SetupController; +import org.briarproject.android.controller.handler.ResultHandler; import org.briarproject.android.util.StrengthMeter; import org.briarproject.api.identity.AuthorConstants; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; import org.robolectric.Robolectric; import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowActivity; -import java.util.logging.Logger; - -import static briarproject.activity.MockedSetupActivity.NO_PASS; -import static briarproject.activity.MockedSetupActivity.QSTRONG_PASS; -import static briarproject.activity.MockedSetupActivity.QWEAK_PASS; -import static briarproject.activity.MockedSetupActivity.STRONG_PASS; -import static briarproject.activity.MockedSetupActivity.WEAK_PASS; import static junit.framework.Assert.assertEquals; -import static org.briarproject.android.util.StrengthMeter.GREEN; -import static org.briarproject.android.util.StrengthMeter.LIME; -import static org.briarproject.android.util.StrengthMeter.ORANGE; -import static org.briarproject.android.util.StrengthMeter.RED; -import static org.briarproject.android.util.StrengthMeter.YELLOW; import static org.briarproject.api.crypto.PasswordStrengthEstimator.NONE; import static org.briarproject.api.crypto.PasswordStrengthEstimator.QUITE_STRONG; import static org.briarproject.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; import static org.briarproject.api.crypto.PasswordStrengthEstimator.STRONG; import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK; -import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.robolectric.Shadows.shadowOf; @RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class SetupActivityTest { - private static final Logger LOG = - Logger.getLogger(SetupActivityTest.class.getName()); - - - private SetupActivity setupActivity; + TestSetupActivity setupActivity; TextInputLayout nicknameEntryWrapper; TextInputLayout passwordEntryWrapper; TextInputLayout passwordConfirmationWrapper; @@ -60,9 +60,15 @@ public class SetupActivityTest { StrengthMeter strengthMeter; Button createAccountButton; + @Mock + SetupController setupController; + @Captor + ArgumentCaptor<ResultHandler<Long>> resultCaptor; + @Before public void setUp() { - setupActivity = Robolectric.setupActivity(MockedSetupActivity.class); + MockitoAnnotations.initMocks(this); + setupActivity = Robolectric.setupActivity(TestSetupActivity.class); nicknameEntryWrapper = (TextInputLayout) setupActivity .findViewById(R.id.nickname_entry_wrapper); passwordEntryWrapper = (TextInputLayout) setupActivity @@ -89,46 +95,134 @@ public class SetupActivityTest { } @Test - public void testUI() { - // Nick - String longNick = - Strings.padEnd("*", AuthorConstants.MAX_AUTHOR_NAME_LENGTH + 1, - '*'); - nicknameEntry.setText(longNick); - assertEquals(nicknameEntryWrapper.getError(), - setupActivity.getString(R.string.name_too_long)); - assertEquals(createAccountButton.isEnabled(), false); - // strength estimator - testStrengthMeter(STRONG_PASS, STRONG, GREEN); - assertEquals(createAccountButton.isEnabled(), false); - testStrengthMeter(QSTRONG_PASS, QUITE_STRONG, LIME); - assertEquals(createAccountButton.isEnabled(), false); - testStrengthMeter(QWEAK_PASS, QUITE_WEAK, YELLOW); - assertEquals(createAccountButton.isEnabled(), false); - testStrengthMeter(WEAK_PASS, WEAK, ORANGE); - assertEquals(createAccountButton.isEnabled(), false); - testStrengthMeter(NO_PASS, NONE, RED); - assertEquals(createAccountButton.isEnabled(), false); - - // pass confirmation - nicknameEntry.setText("nick.nickerton"); + public void testPasswordMatchUI() { + // Password mismatch passwordEntry.setText("really.safe.password"); passwordConfirmation.setText("really.safe.pass"); assertEquals(createAccountButton.isEnabled(), false); assertEquals(passwordConfirmationWrapper.getError(), setupActivity.getString(R.string.passwords_do_not_match)); + // Button enabled passwordEntry.setText("really.safe.pass"); passwordConfirmation.setText("really.safe.pass"); - assertNotEquals(passwordConfirmationWrapper.getError(), + // Confirm that the password mismatch error message is not visible + Assert.assertNotEquals(passwordConfirmationWrapper.getError(), setupActivity.getString(R.string.passwords_do_not_match)); + // Nick has not been set, expect the button to be disabled + assertEquals(createAccountButton.isEnabled(), false); + } + + @Test + public void testCreateAccountUI() { + + SetupController mockedController = this.setupController; + setupActivity.setController(mockedController); + // Mock strong password strength answer + when(mockedController.estimatePasswordStrength(anyString())).thenReturn( + STRONG); + String safePass = "really.safe.password"; + String nick = "nick.nickerton"; + passwordEntry.setText(safePass); + passwordConfirmation.setText(safePass); + nicknameEntry.setText(nick); + // Confirm that the create account button is clickable assertEquals(createAccountButton.isEnabled(), true); - // confirm correct Activity started createAccountButton.performClick(); + // Verify that the controller's method was called with the correct + // params and get the callback + verify(mockedController, times(1)) + .createIdentity(eq(nick), eq(safePass), resultCaptor.capture()); + // execute the callback + resultCaptor.getValue().onResult(1L); assertEquals(setupActivity.isFinishing(), true); + // Confirm that the correct Activity has been started ShadowActivity shadowActivity = shadowOf(setupActivity); Intent intent = shadowActivity.peekNextStartedActivity(); assertEquals(intent.getComponent().getClassName(), NavDrawerActivity.class.getName()); } + @Test + public void testNickUI() { + Assert.assertNotNull(setupActivity); + String longNick = + Strings.padEnd("*", AuthorConstants.MAX_AUTHOR_NAME_LENGTH + 1, + '*'); + nicknameEntry.setText(longNick); + // Password should be too long + assertEquals(nicknameEntryWrapper.getError(), + setupActivity.getString(R.string.name_too_long)); + } + + @Test + public void testAccountCreation() { + SetupController controller = setupActivity.getController(); + // mock a resulthandler + ResultHandler<Long> resultHandler = + (ResultHandler<Long>) mock(ResultHandler.class); + controller + .createIdentity("nick", "some.strong.pass", resultHandler); + // blocking verification call with timeout that waits until the mocked + // result gets called with handle 0L, the expected value + verify(resultHandler, timeout(2000).times(1)).onResult(0L); + SharedPreferences prefs = + setupActivity.getSharedPreferences("db", Context.MODE_PRIVATE); + // Confirm database key + assertTrue(prefs.contains("key")); + // Note that Robolectric uses its own persistant storage that it + // wipes clean after each test run, no need to clean up manually. + } + + @Test + public void testStrengthMeter() { + SetupController controller = setupActivity.getController(); + + String strongPass = "very.strong.password.123"; + String weakPass = "we"; + String quiteStrongPass = "quite.strong"; + + float val = controller.estimatePasswordStrength(strongPass); + assertTrue(val == STRONG); + val = controller.estimatePasswordStrength(weakPass); + assertTrue(val < WEAK && val > NONE); + val = controller.estimatePasswordStrength(quiteStrongPass); + assertTrue(val < STRONG && val > QUITE_WEAK); + } + + @Test + public void testStrengthMeterUI() { + Assert.assertNotNull(setupActivity); + // replace the setup controller with our mocked copy + SetupController mockedController = this.setupController; + setupActivity.setController(mockedController); + // Mock answers for UI testing only + when(mockedController.estimatePasswordStrength("strong")).thenReturn( + STRONG); + when(mockedController.estimatePasswordStrength("qstring")).thenReturn( + QUITE_STRONG); + when(mockedController.estimatePasswordStrength("qweak")).thenReturn( + QUITE_WEAK); + when(mockedController.estimatePasswordStrength("weak")).thenReturn( + WEAK); + when(mockedController.estimatePasswordStrength("empty")).thenReturn( + NONE); + // Test the meters progress and color for several values + testStrengthMeter("strong", STRONG, StrengthMeter.GREEN); + Mockito.verify(mockedController, Mockito.times(1)) + .estimatePasswordStrength(eq("strong")); + testStrengthMeter("qstring", QUITE_STRONG, StrengthMeter.LIME); + Mockito.verify(mockedController, Mockito.times(1)) + .estimatePasswordStrength(eq("qstring")); + testStrengthMeter("qweak", QUITE_WEAK, StrengthMeter.YELLOW); + Mockito.verify(mockedController, Mockito.times(1)) + .estimatePasswordStrength(eq("qweak")); + testStrengthMeter("weak", WEAK, StrengthMeter.ORANGE); + Mockito.verify(mockedController, Mockito.times(1)) + .estimatePasswordStrength(eq("weak")); + // Not sure this should be the correct behaviour on an empty input ? + testStrengthMeter("empty", NONE, StrengthMeter.RED); + Mockito.verify(mockedController, Mockito.times(1)) + .estimatePasswordStrength(eq("empty")); + } + } diff --git a/briar-android/test/java/briarproject/activity/TestSetupActivity.java b/briar-android/test/java/briarproject/activity/TestSetupActivity.java new file mode 100644 index 0000000000..923372a494 --- /dev/null +++ b/briar-android/test/java/briarproject/activity/TestSetupActivity.java @@ -0,0 +1,20 @@ +package briarproject.activity; + +import org.briarproject.android.SetupActivity; +import org.briarproject.android.controller.SetupController; + +/** + * This class exposes the SetupController and offers the possibility to + * override it. + */ +public class TestSetupActivity extends SetupActivity { + + public SetupController getController() { + return this.setupController; + } + + public void setController(SetupController setupController) { + this.setupController = setupController; + } + +} diff --git a/briar-android/test/java/briarproject/controller/SetupControllerTest.java b/briar-android/test/java/briarproject/controller/SetupControllerTest.java deleted file mode 100644 index 85bcfe8095..0000000000 --- a/briar-android/test/java/briarproject/controller/SetupControllerTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package briarproject.controller; - -import org.briarproject.BriarTestCase; -import org.briarproject.android.controller.SetupController; -import org.junit.Before; - -import javax.inject.Inject; - -public class SetupControllerTest extends BriarTestCase { - - - - @Inject - SetupController setupController; - - @Before public void setUp() { - - } -} diff --git a/briar-core/src/org/briarproject/system/LinuxSeedProvider.java b/briar-core/src/org/briarproject/system/LinuxSeedProvider.java index 730beef97a..6481b4a5f2 100644 --- a/briar-core/src/org/briarproject/system/LinuxSeedProvider.java +++ b/briar-core/src/org/briarproject/system/LinuxSeedProvider.java @@ -43,8 +43,6 @@ class LinuxSeedProvider implements SeedProvider { } catch (IOException e) { // On some devices /dev/urandom isn't writable - this isn't fatal if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); - } catch (NullPointerException e) { - if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } // Read the seed from the pool try { -- GitLab