Merge branch 'v/instrument' into v/multi-decrypt

Conflicts:
	.travis.yml
	OpenKeychain/src/androidTest/java/org/sufficientlysecure/keychain/CreateKeyActivityTest.java
	OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerify.java
This commit is contained in:
Vincent Breitmoser
2015-06-17 19:24:07 +02:00
48 changed files with 2636 additions and 492 deletions

View File

@@ -0,0 +1,262 @@
/*
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain;
import android.app.Activity;
import android.content.Intent;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import android.widget.AdapterView;
import org.hamcrest.CoreMatchers;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sufficientlysecure.keychain.service.PassphraseCacheService;
import org.sufficientlysecure.keychain.ui.MainActivity;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.DrawerActions.openDrawer;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.not;
import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
import static org.sufficientlysecure.keychain.TestHelpers.randomString;
import static org.sufficientlysecure.keychain.actions.CustomActions.tokenEncryptViewAddToken;
import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
import static org.sufficientlysecure.keychain.matcher.DrawableMatcher.withDrawable;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class AsymmetricOperationTests {
@Rule
public final ActivityTestRule<MainActivity> mActivity
= new ActivityTestRule<MainActivity>(MainActivity.class) {
@Override
protected Intent getActivityIntent() {
Intent intent = super.getActivityIntent();
intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
return intent;
}
};
@Before
public void setUp() throws Exception {
Activity activity = mActivity.getActivity();
// import these two, make sure they're there
importKeysFromResource(activity, "x.sec.asc");
// make sure no passphrases are cached
PassphraseCacheService.clearCachedPassphrases(activity);
}
@Test
public void testTextEncryptDecryptFromToken() throws Exception {
// navigate to 'encrypt text'
openDrawer(R.id.drawer_layout);
onView(withText(R.string.nav_encrypt_decrypt)).perform(click());
onView(withId(R.id.encrypt_text)).perform(click());
String cleartext = randomString(10, 30);
{ // encrypt
// the EncryptKeyCompletionView is tested individually
onView(withId(R.id.recipient_list)).perform(tokenEncryptViewAddToken(0x9D604D2F310716A3L));
onView(withId(R.id.encrypt_text_text)).perform(typeText(cleartext));
onView(withId(R.id.encrypt_copy)).perform(click());
}
// go to decrypt from clipboard view
pressBack();
onView(withId(R.id.decrypt_from_clipboard)).perform(click());
{ // decrypt
onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
onView(withText(R.string.btn_unlock)).perform(click());
onView(withId(R.id.decrypt_text_plaintext)).check(matches(
withText(cleartext)));
onView(withId(R.id.result_encryption_text)).check(matches(
withText(R.string.decrypt_result_encrypted)));
onView(withId(R.id.result_signature_text)).check(matches(
withText(R.string.decrypt_result_no_signature)));
onView(withId(R.id.result_signature_layout)).check(matches(
not(isDisplayed())));
onView(withId(R.id.result_encryption_icon)).check(matches(
withDrawable(R.drawable.status_lock_closed_24dp)));
onView(withId(R.id.result_signature_icon)).check(matches(
withDrawable(R.drawable.status_signature_unknown_cutout_24dp)));
}
}
@Test
public void testTextEncryptDecryptFromKeyView() throws Exception {
String cleartext = randomString(10, 30);
{ // encrypt
// navigate to edit key dialog
onData(withKeyItemId(0x9D604D2F310716A3L))
.inAdapterView(allOf(isAssignableFrom(AdapterView.class),
isDescendantOfA(withId(R.id.key_list_list))))
.perform(click());
onView(withId(R.id.view_key_action_encrypt_text)).perform(click());
onView(withId(R.id.encrypt_text_text)).perform(typeText(cleartext));
onView(withId(R.id.encrypt_copy)).perform(click());
}
// go to decrypt from clipboard view
pressBack();
pressBack();
openDrawer(R.id.drawer_layout);
onView(withText(R.string.nav_encrypt_decrypt)).perform(click());
onView(withId(R.id.decrypt_from_clipboard)).perform(click());
{ // decrypt
onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
onView(withText(R.string.btn_unlock)).perform(click());
onView(withId(R.id.decrypt_text_plaintext)).check(matches(
withText(cleartext)));
onView(withId(R.id.result_encryption_text)).check(matches(
withText(R.string.decrypt_result_encrypted)));
onView(withId(R.id.result_signature_text)).check(matches(
withText(R.string.decrypt_result_no_signature)));
onView(withId(R.id.result_signature_layout)).check(matches(
not(isDisplayed())));
onView(withId(R.id.result_encryption_icon)).check(matches(
withDrawable(R.drawable.status_lock_closed_24dp)));
onView(withId(R.id.result_signature_icon)).check(matches(
withDrawable(R.drawable.status_signature_unknown_cutout_24dp)));
}
pressBack();
onView(withId(R.id.decrypt_from_clipboard)).perform(click());
{ // decrypt again, passphrase should be cached
onView(withId(R.id.decrypt_text_plaintext)).check(matches(
withText(cleartext)));
onView(withId(R.id.result_encryption_text)).check(matches(
withText(R.string.decrypt_result_encrypted)));
onView(withId(R.id.result_signature_text)).check(matches(
withText(R.string.decrypt_result_no_signature)));
onView(withId(R.id.result_signature_layout)).check(matches(
not(isDisplayed())));
onView(withId(R.id.result_encryption_icon)).check(matches(
withDrawable(R.drawable.status_lock_closed_24dp)));
onView(withId(R.id.result_signature_icon)).check(matches(
withDrawable(R.drawable.status_signature_unknown_cutout_24dp)));
}
}
@Test
public void testSignVerify() throws Exception {
String cleartext = randomString(10, 30);
// navigate to 'encrypt text'
openDrawer(R.id.drawer_layout);
onView(withText(R.string.nav_encrypt_decrypt)).perform(click());
onView(withId(R.id.encrypt_text)).perform(click());
{ // sign
onView(withId(R.id.encrypt_copy)).perform(click());
checkSnackbar(Style.ERROR, R.string.error_empty_text);
// navigate to edit key dialog
onView(withId(R.id.sign)).perform(click());
onData(withKeyItemId(0x9D604D2F310716A3L))
.inAdapterView(isAssignableFrom(AdapterView.class))
.perform(click());
onView(withId(R.id.encrypt_text_text)).perform(typeText(cleartext));
onView(withId(R.id.encrypt_copy)).perform(click());
onView(withId(R.id.passphrase_passphrase)).perform(typeText("x"));
onView(withText(R.string.btn_unlock)).perform(click());
checkSnackbar(Style.OK, R.string.msg_se_success);
}
// go to decrypt from clipboard view
pressBack();
onView(withId(R.id.decrypt_from_clipboard)).perform(click());
{ // decrypt
onView(withId(R.id.decrypt_text_plaintext)).check(matches(
// startsWith because there may be extra newlines
withText(CoreMatchers.startsWith(cleartext))));
onView(withId(R.id.result_encryption_text)).check(matches(
withText(R.string.decrypt_result_not_encrypted)));
onView(withId(R.id.result_signature_text)).check(matches(
withText(R.string.decrypt_result_signature_secret)));
onView(withId(R.id.result_signature_layout)).check(matches(
isDisplayed()));
onView(withId(R.id.result_encryption_icon)).check(matches(
withDrawable(R.drawable.status_lock_open_24dp)));
onView(withId(R.id.result_signature_icon)).check(matches(
withDrawable(R.drawable.status_signature_verified_cutout_24dp)));
}
}
}

View File

@@ -17,11 +17,16 @@
package org.sufficientlysecure.keychain;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import android.text.method.HideReturnsTransformationMethod;
import android.text.method.PasswordTransformationMethod;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sufficientlysecure.keychain.ui.CreateKeyActivity;
import static android.support.test.espresso.Espresso.onView;
@@ -40,6 +45,7 @@ import static org.hamcrest.Matchers.allOf;
import static org.sufficientlysecure.keychain.matcher.EditTextMatchers.withError;
import static org.sufficientlysecure.keychain.matcher.EditTextMatchers.withTransformationMethod;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class CreateKeyActivityTest extends ActivityInstrumentationTestCase2<CreateKeyActivity> {
@@ -52,9 +58,10 @@ public class CreateKeyActivityTest extends ActivityInstrumentationTestCase2<Crea
super(CreateKeyActivity.class);
}
@Override
@Before
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
getActivity();
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain;
import android.app.Activity;
import android.content.Intent;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import android.widget.AdapterView;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.sufficientlysecure.keychain.provider.KeychainDatabase;
import org.sufficientlysecure.keychain.ui.MainActivity;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.allOf;
import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(AndroidJUnit4.class)
@LargeTest
public class EditKeyTest {
@Rule
public final ActivityTestRule<MainActivity> mActivity
= new ActivityTestRule<MainActivity>(MainActivity.class) {
@Override
protected Intent getActivityIntent() {
Intent intent = super.getActivityIntent();
intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
return intent;
}
};
@Test
public void test01Edit() throws Exception {
Activity activity = mActivity.getActivity();
new KeychainDatabase(activity).clearDatabase();
// import key for testing, get a stable initial state
importKeysFromResource(activity, "x.sec.asc");
// navigate to edit key dialog
onData(withKeyItemId(0x9D604D2F310716A3L))
.inAdapterView(allOf(isAssignableFrom(AdapterView.class),
isDescendantOfA(withId(R.id.key_list_list))))
.perform(click());
onView(withId(R.id.menu_key_view_edit)).perform(click());
// no-op should yield snackbar
onView(withText(R.string.btn_save)).perform(click());
checkSnackbar(Style.ERROR, R.string.msg_mf_error_noop);
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain;
import android.content.Intent;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.sufficientlysecure.keychain.ui.MainActivity;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu;
import static android.support.test.espresso.Espresso.pressBack;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.DrawerActions.openDrawer;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.CoreMatchers.not;
import static org.sufficientlysecure.keychain.TestHelpers.checkSnackbar;
import static org.sufficientlysecure.keychain.TestHelpers.randomString;
import static org.sufficientlysecure.keychain.matcher.DrawableMatcher.withDrawable;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(AndroidJUnit4.class)
@LargeTest
public class EncryptDecryptSymmetricTests {
public static final String PASSPHRASE = randomString(5, 20);
@Rule
public final ActivityTestRule<MainActivity> mActivity
= new ActivityTestRule<MainActivity>(MainActivity.class) {
@Override
protected Intent getActivityIntent() {
Intent intent = super.getActivityIntent();
intent.putExtra(MainActivity.EXTRA_SKIP_FIRST_TIME, true);
return intent;
}
};
@Test
public void testSymmetricTextEncryptDecrypt() throws Exception {
MainActivity activity = mActivity.getActivity();
String text = randomString(10, 30);
// navigate to encrypt/decrypt
openDrawer(R.id.drawer_layout);
onView(ViewMatchers.withText(R.string.nav_encrypt_decrypt)).perform(click());
onView(withId(R.id.encrypt_text)).perform(click());
{
onView(withId(R.id.encrypt_text_text)).perform(typeText(text));
openActionBarOverflowOrOptionsMenu(getInstrumentation().getTargetContext());
onView(withText(R.string.label_symmetric)).perform(click());
onView(withId(R.id.passphrase)).perform(typeText(PASSPHRASE));
onView(withId(R.id.encrypt_copy)).perform(click());
checkSnackbar(Style.ERROR, R.string.passphrases_do_not_match);
onView(withId(R.id.passphraseAgain)).perform(typeText(PASSPHRASE));
onView(withId(R.id.encrypt_text_text)).check(matches(withText(text)));
onView(withId(R.id.encrypt_copy)).perform(click());
checkSnackbar(Style.OK, R.string.msg_se_success);
}
// go to decrypt from clipboard view
pressBack();
onView(withId(R.id.decrypt_from_clipboard)).perform(click());
{
onView(withId(R.id.passphrase_passphrase)).perform(typeText(PASSPHRASE));
onView(withText(R.string.btn_unlock)).perform(click());
onView(withId(R.id.decrypt_text_plaintext)).check(matches(
withText(text)));
// TODO write generic status verifier
onView(withId(R.id.result_encryption_text)).check(matches(
withText(R.string.decrypt_result_encrypted)));
onView(withId(R.id.result_signature_text)).check(matches(
withText(R.string.decrypt_result_no_signature)));
onView(withId(R.id.result_signature_layout)).check(matches(
not(isDisplayed())));
onView(withId(R.id.result_encryption_icon)).check(matches(
withDrawable(R.drawable.status_lock_closed_24dp)));
onView(withId(R.id.result_signature_icon)).check(matches(
withDrawable(R.drawable.status_signature_unknown_cutout_24dp)));
}
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain;
import android.app.Activity;
import android.content.Intent;
import android.support.test.espresso.action.ViewActions;
import android.support.test.espresso.matcher.RootMatchers;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.LargeTest;
import android.view.KeyEvent;
import android.widget.AdapterView;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.sufficientlysecure.keychain.ui.EncryptTextActivity;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.CoreMatchers.allOf;
import static org.sufficientlysecure.keychain.TestHelpers.importKeysFromResource;
import static org.sufficientlysecure.keychain.actions.CustomActions.tokenEncryptViewAddToken;
import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyItemId;
import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withKeyToken;
@RunWith(AndroidJUnit4.class)
@LargeTest
public class EncryptKeyCompletionViewTest {
@Rule
public final ActivityTestRule<EncryptTextActivity> mActivity
= new ActivityTestRule<>(EncryptTextActivity.class);
@Test
public void testTextEncryptDecryptFromToken() throws Exception {
Intent intent = new Intent();
intent.putExtra(EncryptTextActivity.EXTRA_ENCRYPTION_KEY_IDS, new long[] { 0x9D604D2F310716A3L });
Activity activity = mActivity.launchActivity(intent);
// import these two, make sure they're there
importKeysFromResource(activity, "x.sec.asc");
// check if the element passed in from intent
onView(withId(R.id.recipient_list)).check(matches(withKeyToken(0x9D604D2F310716A3L)));
onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
// type X, select from list, check if it's there
onView(withId(R.id.recipient_list)).perform(typeText("x"));
onData(withKeyItemId(0x9D604D2F310716A3L)).inRoot(RootMatchers.isPlatformPopup())
.inAdapterView(allOf(isAssignableFrom(AdapterView.class),
hasDescendant(withId(R.id.key_list_item_name)))).perform(click());
onView(withId(R.id.recipient_list)).check(matches(withKeyToken(0x9D604D2F310716A3L)));
onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
// add directly, check if it's there
onView(withId(R.id.recipient_list)).perform(tokenEncryptViewAddToken(0x9D604D2F310716A3L));
onView(withId(R.id.recipient_list)).check(matches(withKeyToken(0x9D604D2F310716A3L)));
onView(withId(R.id.recipient_list)).perform(ViewActions.pressKey(KeyEvent.KEYCODE_DEL));
}
}

View File

@@ -0,0 +1,29 @@
package org.sufficientlysecure.keychain;
import java.lang.reflect.Method;
import android.os.Bundle;
import android.support.test.runner.AndroidJUnitRunner;
public class JacocoWorkaroundJUnitRunner extends AndroidJUnitRunner {
static {
System.setProperty("jacoco-agent.destfile", "/data/data/"
+ BuildConfig.APPLICATION_ID + "/coverage.ec");
}
@Override
public void finish(int resultCode, Bundle results) {
try {
Class rt = Class.forName("org.jacoco.agent.rt.RT");
Method getAgent = rt.getMethod("getAgent");
Method dump = getAgent.getReturnType().getMethod("dump", boolean.class);
Object agent = getAgent.invoke(null);
dump.invoke(agent, false);
} catch (Exception e) {
e.printStackTrace();
}
super.finish(resultCode, results);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sufficientlysecure.keychain;
import java.util.Random;
import android.content.Context;
import android.support.annotation.StringRes;
import org.hamcrest.CoreMatchers;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing.IteratorWithIOThrow;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.ProgressScaler;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.sufficientlysecure.keychain.matcher.CustomMatchers.withSnackbarLineColor;
public class TestHelpers {
public static void checkSnackbar(Style style, @StringRes Integer text) {
onView(withClassName(CoreMatchers.endsWith("Snackbar")))
.check(matches(withSnackbarLineColor(style.mLineColor)));
if (text != null) {
onView(withClassName(CoreMatchers.endsWith("Snackbar")))
.check(matches(hasDescendant(withText(text))));
}
}
static void importKeysFromResource(Context context, String name) throws Exception {
IteratorWithIOThrow<UncachedKeyRing> stream = UncachedKeyRing.fromStream(
getInstrumentation().getContext().getAssets().open(name));
ProviderHelper helper = new ProviderHelper(context);
while(stream.hasNext()) {
UncachedKeyRing ring = stream.next();
if (ring.isSecret()) {
helper.saveSecretKeyRing(ring, new ProgressScaler());
} else {
helper.savePublicKeyRing(ring, new ProgressScaler());
}
}
}
public static String randomString(int min, int max) {
String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789!@#$%^&*()-_=";
Random r = new Random();
StringBuilder passbuilder = new StringBuilder();
// 5% chance for an empty string
for(int i = 0, j = r.nextInt(max)+min; i < j; i++) {
passbuilder.append(chars.charAt(r.nextInt(chars.length())));
}
return passbuilder.toString();
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.sufficientlysecure.keychain.actions;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.espresso.matcher.ViewMatchers;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.view.View;
import com.tokenautocomplete.TokenCompleteTextView;
import org.hamcrest.Matcher;
import org.sufficientlysecure.keychain.pgp.CanonicalizedPublicKeyRing;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter;
import static android.support.test.InstrumentationRegistry.getTargetContext;
public abstract class CustomActions {
public static ViewAction tokenEncryptViewAddToken(long keyId) throws Exception {
CanonicalizedPublicKeyRing ring =
new ProviderHelper(getTargetContext()).getCanonicalizedPublicKeyRing(keyId);
final Object item = new KeyAdapter.KeyItem(ring);
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return ViewMatchers.isAssignableFrom(TokenCompleteTextView.class);
}
@Override
public String getDescription() {
return "add completion token";
}
@Override
public void perform(UiController uiController, View view) {
((TokenCompleteTextView) view).addObject(item);
}
};
}
public static ViewAction tokenViewAddToken(final Object item) {
return new ViewAction() {
@Override
public Matcher<View> getConstraints() {
return ViewMatchers.isAssignableFrom(TokenCompleteTextView.class);
}
@Override
public String getDescription() {
return "add completion token";
}
@Override
public void perform(UiController uiController, View view) {
((TokenCompleteTextView) view).addObject(item);
}
};
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.sufficientlysecure.keychain.matcher;
import android.support.annotation.ColorRes;
import android.support.test.espresso.matcher.BoundedMatcher;
import android.view.View;
import com.nispok.snackbar.Snackbar;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.sufficientlysecure.keychain.EncryptKeyCompletionViewTest;
import org.sufficientlysecure.keychain.ui.adapter.KeyAdapter.KeyItem;
import org.sufficientlysecure.keychain.ui.widget.EncryptKeyCompletionView;
import static android.support.test.internal.util.Checks.checkNotNull;
public abstract class CustomMatchers {
public static Matcher<View> withSnackbarLineColor(@ColorRes final int colorRes) {
return new BoundedMatcher<View, Snackbar>(Snackbar.class) {
public void describeTo(Description description) {
description.appendText("with color resource id: " + colorRes);
}
@Override
public boolean matchesSafely(Snackbar snackbar) {
return snackbar.getResources().getColor(colorRes) == snackbar.getLineColor();
}
};
}
public static Matcher<Object> withKeyItemId(final long keyId) {
return new BoundedMatcher<Object, KeyItem>(KeyItem.class) {
@Override
public boolean matchesSafely(KeyItem item) {
return item.mKeyId == keyId;
}
@Override
public void describeTo(Description description) {
description.appendText("with key id: " + keyId);
}
};
}
public static Matcher<View> withKeyToken(@ColorRes final long keyId) {
return new BoundedMatcher<View, EncryptKeyCompletionView>(EncryptKeyCompletionView.class) {
public void describeTo(Description description) {
description.appendText("with key id token: " + keyId);
}
@Override
public boolean matchesSafely(EncryptKeyCompletionView tokenView) {
for (Object object : tokenView.getObjects()) {
if (object instanceof KeyItem && ((KeyItem) object).mKeyId == keyId) {
return true;
}
}
return false;
}
};
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2015 Xavi Rigau <xrigau@gmail.com>
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* From the droidcon anroid espresso repository.
* https://github.com/xrigau/droidcon-android-espresso/
*
*/
package org.sufficientlysecure.keychain.matcher;
import android.content.res.Resources;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
public class DrawableMatcher extends TypeSafeMatcher<View> {
private final int mResourceId;
private final boolean mIgnoreFilters;
public DrawableMatcher(int resourceId, boolean ignoreFilters) {
super(View.class);
mResourceId = resourceId;
mIgnoreFilters = ignoreFilters;
}
private String resourceName = null;
private Drawable expectedDrawable = null;
@Override
public boolean matchesSafely(View target) {
if (expectedDrawable == null) {
loadDrawableFromResources(target.getResources());
}
if (invalidExpectedDrawable()) {
return false;
}
if (target instanceof ImageView) {
return hasImage((ImageView) target) || hasBackground(target);
}
if (target instanceof TextView) {
return hasCompoundDrawable((TextView) target) || hasBackground(target);
}
return hasBackground(target);
}
private void loadDrawableFromResources(Resources resources) {
try {
expectedDrawable = resources.getDrawable(mResourceId);
resourceName = resources.getResourceEntryName(mResourceId);
} catch (Resources.NotFoundException ignored) {
// view could be from a context unaware of the resource id.
}
}
private boolean invalidExpectedDrawable() {
return expectedDrawable == null;
}
private boolean hasImage(ImageView target) {
return isSameDrawable(target.getDrawable());
}
private boolean hasCompoundDrawable(TextView target) {
for (Drawable drawable : target.getCompoundDrawables()) {
if (isSameDrawable(drawable)) {
return true;
}
}
return false;
}
private boolean hasBackground(View target) {
return isSameDrawable(target.getBackground());
}
private boolean isSameDrawable(Drawable drawable) {
if (drawable == null) {
return false;
}
// if those are both bitmap drawables, compare their bitmaps (ignores color filters, which is what we want!)
if (mIgnoreFilters && drawable instanceof BitmapDrawable && expectedDrawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap().equals(((BitmapDrawable) expectedDrawable).getBitmap());
}
return expectedDrawable.getConstantState().equals(drawable.getConstantState());
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(mResourceId);
if (resourceName != null) {
description.appendText("[");
description.appendText(resourceName);
description.appendText("]");
}
}
public static DrawableMatcher withDrawable(int resourceId, boolean ignoreFilters) {
return new DrawableMatcher(resourceId, ignoreFilters);
}
public static DrawableMatcher withDrawable(int resourceId) {
return new DrawableMatcher(resourceId, true);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2015 Dominik Schürmann <dominik@dominikschuermann.de>
* Copyright (C) 2015 Vincent Breitmoser <look@my.amazin.horse>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by