diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java index ab7cd4cd86bcb8297002785204f6f51a23fb26a8..303d79d1507710f63a99df877234c1356bd566da 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java @@ -33,9 +33,8 @@ public class AuthorNameFragment extends SetupFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getActivity().setTitle(getString(R.string.setup_title)); - View v = - inflater.inflate(R.layout.fragment_setup_author_name, container, - false); + View v = inflater.inflate(R.layout.fragment_setup_author_name, + container, false); authorNameWrapper = (TextInputLayout) v.findViewById(R.id.nickname_entry_wrapper); authorNameInput = @@ -43,7 +42,6 @@ public class AuthorNameFragment extends SetupFragment { nextButton = (Button) v.findViewById(R.id.next); authorNameInput.addTextChangedListener(this); - nextButton.setOnClickListener(this); return v; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java index 328dac981d16af83e6f340d8dc9363a8c22b4703..38376b2a953e7d748cec3c86332b0ed275f4474f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java @@ -35,8 +35,7 @@ public class DozeFragment extends SetupFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getActivity().setTitle(getString(R.string.setup_doze_title)); - View v = - inflater.inflate(R.layout.fragment_setup_doze, container, + View v = inflater.inflate(R.layout.fragment_setup_doze, container, false); dozeButton = (Button) v.findViewById(R.id.dozeButton); progressBar = (ProgressBar) v.findViewById(R.id.progress); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java index 92c80ffe50664d566b719d5cbc53d07f3d9c1486..e2a921d30e8747507840f5e5870f8961594bb4e0 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java @@ -91,7 +91,7 @@ public class PasswordControllerImpl extends ConfigControllerImpl return StringUtils.fromHexString(hex); } - // Call inside cryptoExecutor + @CryptoExecutor String encryptDatabaseKey(SecretKey key, String password) { long now = System.currentTimeMillis(); byte[] encrypted = crypto.encryptWithPassword(key.getBytes(), password); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java index f24cf2daf2dabc4ab675e591a60f7100c34bf992..0106892a15c12c177b1011d9161ad0cf703eeece 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java @@ -37,8 +37,7 @@ public class PasswordFragment extends SetupFragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { getActivity().setTitle(getString(R.string.setup_password_intro)); - View v = - inflater.inflate(R.layout.fragment_setup_password, container, + View v = inflater.inflate(R.layout.fragment_setup_password, container, false); strengthMeter = (StrengthMeter) v.findViewById(R.id.strength_meter); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java index 6a30e737a519604f9178f5ae76743e16e1537bd5..5d3fce3629336819edae16d8ddde874132768749 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java @@ -42,18 +42,20 @@ abstract class SetupFragment extends BaseFragment implements TextWatcher, protected abstract String getHelpText(); @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, - int i2) { + public void beforeTextChanged(CharSequence s, int start, int count, + int after) { // noop } @Override - public void onTextChanged(CharSequence authorName, int i, int i1, int i2) { + public void onTextChanged(CharSequence s, int start, int before, + int count) { // noop } @Override - public boolean onEditorAction(TextView textView, int i, KeyEvent keyEvent) { + public boolean onEditorAction(TextView textView, int actionId, + KeyEvent keyEvent) { onClick(textView); return true; } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java index 08c16f784963c2e37a3439599f8cb3891c90acc6..b6557a693aaa298e8f567021a831cb891ef43dfd 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/navdrawer/NavDrawerActivity.java @@ -192,11 +192,8 @@ public class NavDrawerActivity extends BriarActivity implements drawerLayout.closeDrawer(START); clearBackStack(); loadFragment(item.getItemId()); - //Don't display the Settings Item as checked - if (item.getItemId() == R.id.nav_btn_settings) { - return false; - } - return true; + // Don't display the Settings item as checked + return item.getItemId() != R.id.nav_btn_settings; } @Override @@ -214,7 +211,7 @@ public class NavDrawerActivity extends BriarActivity implements getSupportFragmentManager() .findFragmentByTag(ContactListFragment.TAG) == null) { /* - * This Makes sure that the first fragment (ContactListFragment) the + * This makes sure that the first fragment (ContactListFragment) the * user sees is the same as the last fragment the user sees before * exiting. This models the typical Google navigation behaviour such * as in Gmail/Inbox. diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index 4482526599565dc282c598a1a8a79bc92b27aae7..61991d6e662dd85f718e59ef018b045ff55a54fa 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -172,8 +172,7 @@ public class UiUtils { public static boolean needsDozeWhitelisting(Context ctx) { if (Build.VERSION.SDK_INT < 23) return false; - PowerManager pm = - (PowerManager) ctx.getSystemService(POWER_SERVICE); + PowerManager pm = (PowerManager) ctx.getSystemService(POWER_SERVICE); String packageName = ctx.getPackageName(); if (pm == null) throw new AssertionError(); return !pm.isIgnoringBatteryOptimizations(packageName); diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 0c800fe0c57ebf7d288da7487d116f4731673149..98c44d56c0f9327f86d1290b7885f891414b8630 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -3,7 +3,7 @@ <!-- Setup --> <string name="setup_title">Welcome to Briar</string> - <string name="setup_name_explanation">Choose your user name wisely. You can not change it later and it will be shown next to content you post.\n\nWhen this content is shared, strangers might see your name.</string> + <string name="setup_name_explanation">Your nickname will be shown next to any content you post. You can\'t change it after creating your account.</string> <string name="setup_next">Next</string> <string name="setup_password_intro">Choose a Password</string> <string name="setup_password_explanation">Your Briar account is stored encrypted on your device, not in the cloud. If you forget your password or uninstall Briar, there\'s no way to recover your account.\n\nChoose a long password that\'s hard to guess, such as four random words, or ten random letters, numbers and symbols.</string> diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java index af3cccf2610ea79c1d826a1e5fdd1e57cf672990..a3eca5c2383a4db0a57228792bb372f3368e6e36 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/login/ChangePasswordActivityTest.java @@ -1,7 +1,5 @@ package org.briarproject.briar.android.login; -import android.content.Context; -import android.content.SharedPreferences; import android.support.design.widget.TextInputLayout; import android.widget.Button; import android.widget.EditText; @@ -29,12 +27,8 @@ import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUIT import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG; import static org.briarproject.bramble.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; @@ -45,8 +39,6 @@ import static org.mockito.Mockito.when; packageName = "org.briarproject.briar") public class ChangePasswordActivityTest { - private static final int TIMEOUT_MS = 10 * 1000; - private TestChangePasswordActivity changePasswordActivity; private TextInputLayout passwordConfirmationWrapper; private EditText currentPassword; @@ -69,18 +61,14 @@ public class ChangePasswordActivityTest { .findViewById(R.id.new_password_confirm_wrapper); currentPassword = (EditText) changePasswordActivity .findViewById(R.id.current_password_entry); - newPassword = - (EditText) changePasswordActivity - .findViewById(R.id.new_password_entry); - newPasswordConfirmation = - (EditText) changePasswordActivity - .findViewById(R.id.new_password_confirm); - strengthMeter = - (StrengthMeter) changePasswordActivity - .findViewById(R.id.strength_meter); - changePasswordButton = - (Button) changePasswordActivity - .findViewById(R.id.change_password); + newPassword = (EditText) changePasswordActivity + .findViewById(R.id.new_password_entry); + newPasswordConfirmation = (EditText) changePasswordActivity + .findViewById(R.id.new_password_confirm); + strengthMeter = (StrengthMeter) changePasswordActivity + .findViewById(R.id.strength_meter); + changePasswordButton = (Button) changePasswordActivity + .findViewById(R.id.change_password); } private void testStrengthMeter(String pass, float strength, int color) { @@ -134,90 +122,38 @@ public class ChangePasswordActivityTest { assertEquals(changePasswordActivity.isFinishing(), true); } - @Test - public void testPasswordChange() { - PasswordController passwordController = - changePasswordActivity.getPasswordController(); - - TestSetupActivity setupActivity = - Robolectric.setupActivity(TestSetupActivity.class); - SetupController setupController = setupActivity.getController(); - setupController.setAuthorName("nick"); - setupController.setPassword("some.old.pass"); - // mock a resulthandler - ResultHandler<Void> resultHandler = mock(ResultHandler.class); - setupController.createAccount(resultHandler); - verify(resultHandler, timeout(TIMEOUT_MS).times(1)).onResult(null); - - SharedPreferences prefs = changePasswordActivity - .getSharedPreferences("db", Context.MODE_PRIVATE); - // Confirm database key - assertTrue(prefs.contains("key")); - String oldKey = prefs.getString("key", null); - // mock a resulthandler - ResultHandler<Boolean> resultHandler2 = mock(ResultHandler.class); - passwordController.changePassword("some.old.pass", "some.strong.pass", - resultHandler2); - // blocking verification call with timeout that waits until the mocked - // result gets called with handle 0L, the expected value - verify(resultHandler2, timeout(TIMEOUT_MS).times(1)).onResult(true); - // Confirm database key - assertTrue(prefs.contains("key")); - assertNotEquals(oldKey, prefs.getString("key", null)); - // Note that Robolectric uses its own persistent storage that it - // wipes clean after each test run, no need to clean up manually. - } - - @Test - public void testStrengthMeter() { - PasswordController controller = - changePasswordActivity.getPasswordController(); - - 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(changePasswordActivity); // replace the password controller with our mocked copy - PasswordController mockedController = this.passwordController; - changePasswordActivity.setPasswordController(mockedController); + changePasswordActivity.setPasswordController(passwordController); // Mock answers for UI testing only - when(mockedController.estimatePasswordStrength("strong")).thenReturn( + when(passwordController.estimatePasswordStrength("strong")).thenReturn( STRONG); - when(mockedController.estimatePasswordStrength("qstrong")).thenReturn( + when(passwordController.estimatePasswordStrength("qstrong")).thenReturn( QUITE_STRONG); - when(mockedController.estimatePasswordStrength("qweak")).thenReturn( + when(passwordController.estimatePasswordStrength("qweak")).thenReturn( QUITE_WEAK); - when(mockedController.estimatePasswordStrength("weak")).thenReturn( + when(passwordController.estimatePasswordStrength("weak")).thenReturn( WEAK); - when(mockedController.estimatePasswordStrength("empty")).thenReturn( + when(passwordController.estimatePasswordStrength("empty")).thenReturn( NONE); // Test the meters progress and color for several values testStrengthMeter("strong", STRONG, StrengthMeter.GREEN); - Mockito.verify(mockedController, Mockito.times(1)) + Mockito.verify(passwordController, Mockito.times(1)) .estimatePasswordStrength(eq("strong")); testStrengthMeter("qstrong", QUITE_STRONG, StrengthMeter.LIME); - Mockito.verify(mockedController, Mockito.times(1)) + Mockito.verify(passwordController, Mockito.times(1)) .estimatePasswordStrength(eq("qstrong")); testStrengthMeter("qweak", QUITE_WEAK, StrengthMeter.YELLOW); - Mockito.verify(mockedController, Mockito.times(1)) + Mockito.verify(passwordController, Mockito.times(1)) .estimatePasswordStrength(eq("qweak")); testStrengthMeter("weak", WEAK, StrengthMeter.ORANGE); - Mockito.verify(mockedController, Mockito.times(1)) + Mockito.verify(passwordController, 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)) + Mockito.verify(passwordController, Mockito.times(1)) .estimatePasswordStrength(eq("empty")); } diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..34ad19fc5a7c97b3036bd1fa5bba89a75b4f83b6 --- /dev/null +++ b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java @@ -0,0 +1,99 @@ +package org.briarproject.briar.android.login; + +import android.content.SharedPreferences; + +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; +import org.briarproject.bramble.api.db.DatabaseConfig; +import org.briarproject.bramble.test.BrambleMockTestCase; +import org.briarproject.bramble.test.ImmediateExecutor; +import org.briarproject.briar.android.controller.handler.ResultHandler; +import org.jmock.Expectations; +import org.junit.Test; + +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; +import static org.briarproject.bramble.test.TestUtils.getSecretKey; + +public class PasswordControllerImplTest extends BrambleMockTestCase { + + private final SharedPreferences briarPrefs = + context.mock(SharedPreferences.class); + private final DatabaseConfig databaseConfig = + context.mock(DatabaseConfig.class); + private final CryptoComponent crypto = context.mock(CryptoComponent.class); + private final PasswordStrengthEstimator estimator = + context.mock(PasswordStrengthEstimator.class); + private final SharedPreferences.Editor editor = + context.mock(SharedPreferences.Editor.class); + + private final Executor cryptoExecutor = new ImmediateExecutor(); + + private final String oldPassword = "some.old.pass"; + private final String newPassword = "some.new.pass"; + private final String oldEncryptedHex = "010203"; + private final String newEncryptedHex = "020304"; + private final byte[] oldEncryptedBytes = new byte[] {1, 2, 3}; + private final byte[] newEncryptedBytes = new byte[] {2, 3, 4}; + private final byte[] keyBytes = getSecretKey().getBytes(); + + @Test + public void testChangePasswordReturnsTrue() { + context.checking(new Expectations() {{ + // Look up the encrypted DB key + oneOf(briarPrefs).getString("key", null); + will(returnValue(oldEncryptedHex)); + // Decrypt and re-encrypt the key + oneOf(crypto).decryptWithPassword(oldEncryptedBytes, oldPassword); + will(returnValue(keyBytes)); + oneOf(crypto).encryptWithPassword(keyBytes, newPassword); + will(returnValue(newEncryptedBytes)); + // Store the re-encrypted key + oneOf(briarPrefs).edit(); + will(returnValue(editor)); + oneOf(editor).putString("key", newEncryptedHex); + oneOf(editor).apply(); + }}); + + PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs, + databaseConfig, cryptoExecutor, crypto, estimator); + + final AtomicBoolean capturedResult = new AtomicBoolean(false); + p.changePassword(oldPassword, newPassword, + new ResultHandler<Boolean>() { + @Override + public void onResult(Boolean result) { + capturedResult.set(result); + } + }); + assertTrue(capturedResult.get()); + } + + @Test + public void testChangePasswordReturnsFalseIfOldPasswordIsWrong() { + context.checking(new Expectations() {{ + // Look up the encrypted DB key + oneOf(briarPrefs).getString("key", null); + will(returnValue(oldEncryptedHex)); + // Try to decrypt the key - the password is wrong + oneOf(crypto).decryptWithPassword(oldEncryptedBytes, oldPassword); + will(returnValue(null)); + }}); + + PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs, + databaseConfig, cryptoExecutor, crypto, estimator); + + final AtomicBoolean capturedResult = new AtomicBoolean(true); + p.changePassword(oldPassword, newPassword, + new ResultHandler<Boolean>() { + @Override + public void onResult(Boolean result) { + capturedResult.set(result); + } + }); + assertFalse(capturedResult.get()); + } +} diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java index 9fb2be4fcf361a775ac2baca17e417d976d862e4..21355f07f1787c80df751e7191f7bb7f2fbad5de 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java @@ -1,5 +1,6 @@ package org.briarproject.briar.android.login; +import android.support.design.widget.TextInputLayout; import android.view.View; import android.widget.Button; import android.widget.EditText; @@ -16,8 +17,12 @@ import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.annotation.Config; import static junit.framework.Assert.assertEquals; +import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.NONE; +import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG; +import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG; -import static org.mockito.ArgumentMatchers.anyString; +import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK; +import static org.junit.Assert.assertNotEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,6 +37,8 @@ public class PasswordFragmentTest { private PasswordFragment passwordFragment = new PasswordFragment(); private EditText passwordEntry; private EditText passwordConfirmation; + private TextInputLayout passwordConfirmationWrapper; + private StrengthMeter strengthMeter; private Button createAccountButton; @Mock @@ -45,17 +52,21 @@ public class PasswordFragmentTest { View v = passwordFragment.getView(); passwordEntry = (EditText) v.findViewById(R.id.password_entry); passwordConfirmation = (EditText) v.findViewById(R.id.password_confirm); + passwordConfirmationWrapper = + (TextInputLayout) v.findViewById(R.id.password_confirm_wrapper); + strengthMeter = (StrengthMeter) v.findViewById(R.id.strength_meter); createAccountButton = (Button) v.findViewById(R.id.next); } @Test public void testCreateAccountUI() { + String safePass = "really.safe.password"; + passwordFragment.setupController = setupController; when(setupController.needsDozeWhitelisting()).thenReturn(false); - when(setupController.estimatePasswordStrength(anyString())) + when(setupController.estimatePasswordStrength(safePass)) .thenReturn(STRONG); - String safePass = "really.safe.password"; passwordEntry.setText(safePass); passwordConfirmation.setText(safePass); // Confirm that the create account button is clickable @@ -63,10 +74,44 @@ public class PasswordFragmentTest { createAccountButton.performClick(); // assert controller has been called properly - verify(setupController, times(1)) - .setPassword(safePass); - verify(setupController, times(1)) - .showDozeOrCreateAccount(); + verify(setupController, times(1)).setPassword(safePass); + verify(setupController, times(1)).showDozeOrCreateAccount(); + } + + @Test + public void testStrengthMeterUI() { + // Test the meters' progress and color for several values + testStrengthMeter("1234567890ab", STRONG, StrengthMeter.GREEN); + testStrengthMeter("123456789", QUITE_STRONG, StrengthMeter.LIME); + testStrengthMeter("123456", QUITE_WEAK, StrengthMeter.YELLOW); + testStrengthMeter("123", WEAK, StrengthMeter.ORANGE); + testStrengthMeter("", NONE, StrengthMeter.RED); + } + + private void testStrengthMeter(String pass, float strength, int color) { + passwordEntry.setText(pass); + assertEquals(strengthMeter.getProgress(), + (int) (strengthMeter.getMax() * strength)); + assertEquals(color, strengthMeter.getColor()); + } + + + @Test + public void testPasswordMatchUI() { + // Password mismatch + passwordEntry.setText("really.safe.password"); + passwordConfirmation.setText("really.safe.pass"); + assertEquals(createAccountButton.isEnabled(), false); + assertEquals(passwordConfirmationWrapper.getError(), + passwordFragment.getString(R.string.passwords_do_not_match)); + // Button enabled + passwordEntry.setText("really.safe.pass"); + passwordConfirmation.setText("really.safe.pass"); + // Confirm that the password mismatch error message is not visible + assertNotEquals(passwordConfirmationWrapper.getError(), + passwordFragment.getString(R.string.passwords_do_not_match)); + // Passwords match, so button should be enabled + assertEquals(createAccountButton.isEnabled(), true); } } diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java index 1d3c9336ba1b7f7d6f0ab0accbff6e482580802a..bf911b3fee662715170c064d7d09a9be225447fa 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java @@ -1,9 +1,6 @@ package org.briarproject.briar.android.login; -import android.content.Context; -import android.content.SharedPreferences; import android.support.design.widget.TextInputLayout; -import android.widget.Button; import android.widget.EditText; import com.google.common.base.Strings; @@ -12,7 +9,6 @@ import org.briarproject.bramble.api.identity.AuthorConstants; import org.briarproject.briar.BuildConfig; import org.briarproject.briar.R; import org.briarproject.briar.android.TestBriarApplication; -import org.briarproject.briar.android.controller.handler.ResultHandler; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -23,15 +19,6 @@ import org.robolectric.RobolectricGradleTestRunner; import org.robolectric.annotation.Config; import static junit.framework.Assert.assertEquals; -import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.NONE; -import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG; -import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; -import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG; -import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.WEAK; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.verify; @RunWith(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21, @@ -39,26 +26,18 @@ import static org.mockito.Mockito.verify; packageName = "org.briarproject.briar") public class SetupActivityTest { - private static final int TIMEOUT_MS = 10 * 1000; - - private TestSetupActivity setupActivity; + private SetupActivity setupActivity; private TextInputLayout nicknameEntryWrapper; - private TextInputLayout passwordConfirmationWrapper; private EditText nicknameEntry; - private EditText passwordEntry; - private EditText passwordConfirmation; - private StrengthMeter strengthMeter; - private Button createAccountButton; @Before public void setUp() { MockitoAnnotations.initMocks(this); - setupActivity = Robolectric.setupActivity(TestSetupActivity.class); + setupActivity = Robolectric.setupActivity(SetupActivity.class); nicknameEntryWrapper = (TextInputLayout) setupActivity .findViewById(R.id.nickname_entry_wrapper); nicknameEntry = (EditText) setupActivity.findViewById(R.id.nickname_entry); - createAccountButton = (Button) setupActivity.findViewById(R.id.next); } @Test @@ -72,92 +51,4 @@ public class SetupActivityTest { assertEquals(nicknameEntryWrapper.getError(), setupActivity.getString(R.string.name_too_long)); } - - @Test - public void testPasswordMatchUI() { - proceedToPasswordFragment(); - // 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"); - // Confirm that the password mismatch error message is not visible - Assert.assertNotEquals(passwordConfirmationWrapper.getError(), - setupActivity.getString(R.string.passwords_do_not_match)); - // Passwords match, so button should be enabled - assertEquals(createAccountButton.isEnabled(), true); - } - - @Test - public void testAccountCreation() { - SetupController controller = setupActivity.getController(); - controller.setAuthorName("nick"); - controller.setPassword("password"); - // mock a resulthandler - ResultHandler<Void> resultHandler = mock(ResultHandler.class); - controller.createAccount(resultHandler); - verify(resultHandler, timeout(TIMEOUT_MS).times(1)).onResult(null); - SharedPreferences prefs = - setupActivity.getSharedPreferences("db", Context.MODE_PRIVATE); - // Confirm database key - assertTrue(prefs.contains("key")); - // Note that Robolectric uses its own persistent 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() { - proceedToPasswordFragment(); - // Test the meters progress and color for several values - testStrengthMeter("1234567890ab", STRONG, StrengthMeter.GREEN); - testStrengthMeter("123456789", QUITE_STRONG, StrengthMeter.LIME); - testStrengthMeter("123456", QUITE_WEAK, StrengthMeter.YELLOW); - testStrengthMeter("123", WEAK, StrengthMeter.ORANGE); - testStrengthMeter("", NONE, StrengthMeter.RED); - } - - private void proceedToPasswordFragment() { - // proceed to password fragment - nicknameEntry.setText("nick"); - createAccountButton.performClick(); - - // find UI elements in new fragment - strengthMeter = - (StrengthMeter) setupActivity.findViewById(R.id.strength_meter); - passwordConfirmationWrapper = (TextInputLayout) setupActivity - .findViewById(R.id.password_confirm_wrapper); - passwordEntry = - (EditText) setupActivity.findViewById(R.id.password_entry); - passwordConfirmation = - (EditText) setupActivity.findViewById(R.id.password_confirm); - createAccountButton = (Button) setupActivity.findViewById(R.id.next); - } - - private void testStrengthMeter(String pass, float strength, int color) { - passwordEntry.setText(pass); - assertEquals(strengthMeter.getProgress(), - (int) (strengthMeter.getMax() * strength)); - assertEquals(color, strengthMeter.getColor()); - } - } diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f181e2881439e44f55bc56436b201ab579447853 --- /dev/null +++ b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java @@ -0,0 +1,86 @@ +package org.briarproject.briar.android.login; + +import android.content.SharedPreferences; + +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.db.DatabaseConfig; +import org.briarproject.bramble.test.BrambleMockTestCase; +import org.briarproject.bramble.test.ImmediateExecutor; +import org.briarproject.briar.android.controller.handler.ResultHandler; +import org.jmock.Expectations; +import org.jmock.lib.legacy.ClassImposteriser; +import org.junit.Test; + +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; + +import static junit.framework.Assert.assertTrue; +import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; +import static org.briarproject.bramble.test.TestUtils.getSecretKey; +import static org.briarproject.bramble.util.StringUtils.getRandomString; + +public class SetupControllerImplTest extends BrambleMockTestCase { + + private final SharedPreferences briarPrefs = + context.mock(SharedPreferences.class); + private final DatabaseConfig databaseConfig = + context.mock(DatabaseConfig.class); + private final CryptoComponent crypto = context.mock(CryptoComponent.class); + private final PasswordStrengthEstimator estimator = + context.mock(PasswordStrengthEstimator.class); + private final SharedPreferences.Editor editor = + context.mock(SharedPreferences.Editor.class); + private final SetupActivity setupActivity; + + private final Executor cryptoExecutor = new ImmediateExecutor(); + + private final String authorName = getRandomString(MAX_AUTHOR_NAME_LENGTH); + private final String password = "some.strong.pass"; + private final String encryptedHex = "010203"; + private final byte[] encryptedBytes = new byte[] {1, 2, 3}; + private final SecretKey key = getSecretKey(); + + public SetupControllerImplTest() { + context.setImposteriser(ClassImposteriser.INSTANCE); + setupActivity = context.mock(SetupActivity.class); + } + + @Test + public void testCreateAccount() { + context.checking(new Expectations() {{ + // Setting the author name shows the password fragment + oneOf(setupActivity).showPasswordFragment(); + // Generate a database key + oneOf(crypto).generateSecretKey(); + will(returnValue(key)); + // Attach the author name and database key to the database config + oneOf(databaseConfig).setLocalAuthorName(authorName); + oneOf(databaseConfig).setEncryptionKey(key); + // Encrypt the key with the password + oneOf(crypto).encryptWithPassword(key.getBytes(), password); + will(returnValue(encryptedBytes)); + // Store the encrypted key + oneOf(briarPrefs).edit(); + will(returnValue(editor)); + oneOf(editor).putString("key", encryptedHex); + oneOf(editor).apply(); + }}); + + SetupControllerImpl s = new SetupControllerImpl(briarPrefs, + databaseConfig, cryptoExecutor, crypto, estimator); + s.setSetupActivity(setupActivity); + + final AtomicBoolean called = new AtomicBoolean(false); + s.setAuthorName(authorName); + s.setPassword(password); + s.createAccount(new ResultHandler<Void>() { + @Override + public void onResult(Void result) { + called.set(true); + } + }); + assertTrue(called.get()); + } +} diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java b/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java index ac4e60a88fe02bdc9ef78a0438dab48b74a04ebf..c27ad7e118c33d5f24ac06e97e1f7fac7e087775 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/login/TestChangePasswordActivity.java @@ -1,15 +1,11 @@ package org.briarproject.briar.android.login; /** - * This class exposes the PasswordController and SetupController and offers the - * possibility to override them. + * This class exposes the PasswordController and offers the possibility to + * replace it. */ public class TestChangePasswordActivity extends ChangePasswordActivity { - public PasswordController getPasswordController() { - return passwordController; - } - public void setPasswordController(PasswordController passwordController) { this.passwordController = passwordController; } diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/TestSetupActivity.java b/briar-android/src/test/java/org/briarproject/briar/android/login/TestSetupActivity.java deleted file mode 100644 index 0592a736e4353e22622b0df393a9cd989a964d94..0000000000000000000000000000000000000000 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/TestSetupActivity.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.briarproject.briar.android.login; - -/** - * This class exposes the SetupController and offers the possibility to - * override it. - */ -public class TestSetupActivity extends SetupActivity { - - SetupController getController() { - return setupController; - } - -}