diff --git a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java index bb58799b6..63c24088e 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java @@ -159,7 +159,8 @@ class AccountManagerImpl implements AccountManager { @Override public boolean createAccount(String name, String password) { synchronized (stateChangeLock) { - // TODO don't allow creating another account if one already exists + if (hasDatabaseKey()) + throw new AssertionError("Already have a database key"); LocalAuthor localAuthor = identityManager.createLocalAuthor(name); identityManager.registerLocalAuthor(localAuthor); SecretKey key = crypto.generateSecretKey(); @@ -182,6 +183,7 @@ class AccountManagerImpl implements AccountManager { LOG.info("Deleting account"); IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory()); IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory()); + databaseKey = null; } } diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 1bb45a77c..ba6c06b31 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -2,65 +2,6 @@ apply plugin: 'com.android.application' apply plugin: 'witness' apply from: 'witness.gradle' -dependencies { - implementation project(path: ':briar-core', configuration: 'default') - implementation project(path: ':bramble-core', configuration: 'default') - implementation project(':bramble-android') - - def supportVersion = '27.1.1' - implementation "com.android.support:support-v4:$supportVersion" - implementation("com.android.support:appcompat-v7:$supportVersion") { - exclude module: 'support-v4' - } - implementation("com.android.support:preference-v14:$supportVersion") { - exclude module: 'support-v4' - } - implementation("com.android.support:design:$supportVersion") { - exclude module: 'support-v4' - exclude module: 'recyclerview-v7' - } - implementation "com.android.support:cardview-v7:$supportVersion" - implementation "com.android.support:support-annotations:$supportVersion" - implementation 'com.android.support.constraint:constraint-layout:1.1.3' - - implementation('ch.acra:acra:4.9.1') { - exclude module: 'support-v4' - exclude module: 'support-annotations' - } - implementation 'info.guardianproject.panic:panic:0.5' - implementation 'info.guardianproject.trustedintents:trustedintents:0.2' - implementation 'de.hdodenhof:circleimageview:2.2.0' - implementation 'com.google.zxing:core:3.3.0' - implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0' - implementation 'com.vanniktech:emoji-google:0.5.1' - - annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2' - - compileOnly 'javax.annotation:jsr250-api:1.0' - - testImplementation project(path: ':bramble-api', configuration: 'testOutput') - testImplementation project(path: ':bramble-core', configuration: 'testOutput') - testImplementation 'org.robolectric:robolectric:3.8' - testImplementation 'org.robolectric:shadows-support-v4:3.3.2' - testImplementation 'org.mockito:mockito-core:2.13.0' - testImplementation 'junit:junit:4.12' - testImplementation "org.jmock:jmock:2.8.2" - testImplementation "org.jmock:jmock-junit4:2.8.2" - testImplementation "org.jmock:jmock-legacy:2.8.2" - testImplementation "org.hamcrest:hamcrest-library:1.3" - testImplementation "org.hamcrest:hamcrest-core:1.3" - - def espressoVersion = '3.0.2' - androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion" - androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion" - androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion" - androidTestImplementation "tools.fastlane:screengrab:1.1.0" - androidTestImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.3" - androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.0.2" - androidTestCompileOnly 'javax.annotation:jsr250-api:1.0' - androidTestImplementation 'junit:junit:4.12' -} - def getStdout = { command, defaultValue -> def stdout = new ByteArrayOutputStream() try { @@ -89,7 +30,7 @@ android { def now = (long) (System.currentTimeMillis() / 1000) buildConfigField "Long", "BuildTimestamp", "${getStdout(['git', 'log', '-n', '1', '--format=%ct'], now)}000L" - testInstrumentationRunner 'org.briarproject.briar.android.test.BriarTestRunner' + testInstrumentationRunner 'org.briarproject.briar.android.BriarTestRunner' } buildTypes { @@ -144,6 +85,65 @@ android { } } +dependencies { + implementation project(path: ':briar-core', configuration: 'default') + implementation project(path: ':bramble-core', configuration: 'default') + implementation project(':bramble-android') + + def supportVersion = '27.1.1' + implementation "com.android.support:support-v4:$supportVersion" + implementation("com.android.support:appcompat-v7:$supportVersion") { + exclude module: 'support-v4' + } + implementation("com.android.support:preference-v14:$supportVersion") { + exclude module: 'support-v4' + } + implementation("com.android.support:design:$supportVersion") { + exclude module: 'support-v4' + exclude module: 'recyclerview-v7' + } + implementation "com.android.support:cardview-v7:$supportVersion" + implementation "com.android.support:support-annotations:$supportVersion" + implementation 'com.android.support.constraint:constraint-layout:1.1.3' + + implementation('ch.acra:acra:4.9.1') { + exclude module: 'support-v4' + exclude module: 'support-annotations' + } + implementation 'info.guardianproject.panic:panic:0.5' + implementation 'info.guardianproject.trustedintents:trustedintents:0.2' + implementation 'de.hdodenhof:circleimageview:2.2.0' + implementation 'com.google.zxing:core:3.3.0' + implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0' + implementation 'com.vanniktech:emoji-google:0.5.1' + + annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2' + + compileOnly 'javax.annotation:jsr250-api:1.0' + + testImplementation project(path: ':bramble-api', configuration: 'testOutput') + testImplementation project(path: ':bramble-core', configuration: 'testOutput') + testImplementation 'org.robolectric:robolectric:3.8' + testImplementation 'org.robolectric:shadows-support-v4:3.3.2' + testImplementation 'org.mockito:mockito-core:2.13.0' + testImplementation 'junit:junit:4.12' + testImplementation "org.jmock:jmock:2.8.2" + testImplementation "org.jmock:jmock-junit4:2.8.2" + testImplementation "org.jmock:jmock-legacy:2.8.2" + testImplementation "org.hamcrest:hamcrest-library:1.3" + testImplementation "org.hamcrest:hamcrest-core:1.3" + + def espressoVersion = '3.0.2' + androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion" + androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion" + androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion" + androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.0.2" + androidTestCompileOnly 'javax.annotation:jsr250-api:1.0' + androidTestImplementation 'junit:junit:4.12' + androidTestScreenshotImplementation "tools.fastlane:screengrab:1.2.0" + androidTestScreenshotImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.3" +} + task verifyTranslations { doLast { def file = project.file("src/main/res/values/arrays.xml") diff --git a/briar-android/fastlane/Screengrabfile b/briar-android/fastlane/Screengrabfile index bacf6905b..53f4226de 100644 --- a/briar-android/fastlane/Screengrabfile +++ b/briar-android/fastlane/Screengrabfile @@ -1,9 +1,7 @@ app_package_name "org.briarproject.briar.android.screenshot.debug" locales ['en-US'] -use_tests_in_classes([ - 'org.briarproject.briar.android.login.SetupActivityScreenshotTest', - 'org.briarproject.briar.android.settings.SettingsActivityScreenshotTest', -]) app_apk_path "build/outputs/apk/screenshot/debug/briar-android-screenshot-debug.apk" tests_apk_path "build/outputs/apk/androidTest/screenshot/debug/briar-android-screenshot-debug-androidTest.apk" -test_instrumentation_runner "org.briarproject.briar.android.test.BriarTestRunner" \ No newline at end of file +test_instrumentation_runner "org.briarproject.briar.android.BriarTestRunner" +reinstall_app = true +exit_on_test_failure = true \ No newline at end of file diff --git a/briar-android/fastlane/demo-mode-activate.sh b/briar-android/fastlane/demo-mode-activate.sh index 23001fb56..41dbe273e 100755 --- a/briar-android/fastlane/demo-mode-activate.sh +++ b/briar-android/fastlane/demo-mode-activate.sh @@ -4,4 +4,8 @@ adb shell am broadcast -a com.android.systemui.demo -e command enter adb shell am broadcast -a com.android.systemui.demo -e command notifications -e visible false adb shell am broadcast -a com.android.systemui.demo -e command battery -e level 100 adb shell am broadcast -a com.android.systemui.demo -e command network -e wifi show -adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1337 \ No newline at end of file +adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 1337 + +# workaround for Android Pie hidden API Espresso bug +adb shell settings put global hidden_api_policy_pre_p_apps 1 +adb shell settings put global hidden_api_policy_p_apps 1 diff --git a/briar-android/proguard-test.txt b/briar-android/proguard-test.txt index df4eb272e..91b89ae58 100644 --- a/briar-android/proguard-test.txt +++ b/briar-android/proguard-test.txt @@ -12,4 +12,4 @@ -keep class junit.** { *; } -dontwarn junit.** --dontwarn org.briarproject.briar.android.BriarTestComponentApplication \ No newline at end of file +-dontwarn org.briarproject.briar.android.** diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/BriarTestRunner.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/BriarTestRunner.java similarity index 63% rename from briar-android/src/androidTest/java/org/briarproject/briar/android/test/BriarTestRunner.java rename to briar-android/src/androidTest/java/org/briarproject/briar/android/BriarTestRunner.java index bf520102d..4d1081b42 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/BriarTestRunner.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/BriarTestRunner.java @@ -1,11 +1,9 @@ -package org.briarproject.briar.android.test; +package org.briarproject.briar.android; import android.app.Application; import android.content.Context; import android.support.test.runner.AndroidJUnitRunner; -import org.briarproject.briar.android.BriarTestComponentApplication; - public class BriarTestRunner extends AndroidJUnitRunner { @Override @@ -13,8 +11,8 @@ public class BriarTestRunner extends AndroidJUnitRunner { Context context) throws InstantiationException, IllegalAccessException, ClassNotFoundException { - return super.newApplication(cl, BriarTestComponentApplication.class.getName(), - context); + return super.newApplication(cl, + BriarTestComponentApplication.class.getName(), context); } } diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/UiTest.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/UiTest.java new file mode 100644 index 000000000..7d9249cfb --- /dev/null +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/UiTest.java @@ -0,0 +1,79 @@ +package org.briarproject.briar.android; + +import android.app.Activity; +import android.content.Intent; +import android.support.test.espresso.intent.rule.IntentsTestRule; + +import org.briarproject.bramble.api.account.AccountManager; +import org.briarproject.bramble.api.lifecycle.LifecycleManager; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.R; + +import javax.annotation.Nullable; +import javax.inject.Inject; + +import static android.support.test.InstrumentationRegistry.getTargetContext; + +@SuppressWarnings("WeakerAccess") +public abstract class UiTest { + + protected final String USERNAME = + getTargetContext().getString(R.string.screenshot_alice); + protected static final String PASSWORD = "123456"; + + @Inject + protected AccountManager accountManager; + @Inject + protected LifecycleManager lifecycleManager; + + public UiTest() { + BriarTestComponentApplication app = + (BriarTestComponentApplication) getTargetContext() + .getApplicationContext(); + inject((BriarUiTestComponent) app.getApplicationComponent()); + } + + protected abstract void inject(BriarUiTestComponent component); + + @NotNullByDefault + protected class CleanAccountTestRule + extends IntentsTestRule { + + @Nullable + private final Runnable runnable; + + public CleanAccountTestRule(Class activityClass) { + super(activityClass); + this.runnable = null; + } + + /** + * Use this if you need to run code before launching the activity. + * Note: You need to use {@link #launchActivity(Intent)} yourself + * to start the activity. + */ + public CleanAccountTestRule(Class activityClass, Runnable runnable) { + super(activityClass, false, false); + this.runnable = runnable; + } + + @Override + protected void beforeActivityLaunched() { + super.beforeActivityLaunched(); + accountManager.deleteAccount(); + accountManager.createAccount(USERNAME, PASSWORD); + if (runnable != null) { + Intent serviceIntent = + new Intent(getTargetContext(), BriarService.class); + getTargetContext().startService(serviceIntent); + try { + lifecycleManager.waitForStartup(); + } catch (InterruptedException e) { + throw new AssertionError(e); + } + runnable.run(); + } + } + } + +} diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ViewActions.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/ViewActions.java similarity index 98% rename from briar-android/src/androidTest/java/org/briarproject/briar/android/test/ViewActions.java rename to briar-android/src/androidTest/java/org/briarproject/briar/android/ViewActions.java index 0d1aceba5..ac3edab65 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ViewActions.java +++ b/briar-android/src/androidTest/java/org/briarproject/briar/android/ViewActions.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.test; +package org.briarproject.briar.android; import android.app.Activity; import android.support.test.espresso.PerformException; diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ScreenshotTest.java b/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ScreenshotTest.java deleted file mode 100644 index 355dd7763..000000000 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/test/ScreenshotTest.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.briarproject.briar.android.test; - -import android.app.Activity; -import android.support.test.espresso.intent.rule.IntentsTestRule; -import android.util.Log; - -import org.briarproject.bramble.api.account.AccountManager; -import org.briarproject.bramble.api.lifecycle.LifecycleManager; -import org.briarproject.briar.android.BriarTestComponentApplication; -import org.briarproject.briar.android.BriarUiTestComponent; -import org.junit.ClassRule; - -import javax.inject.Inject; - -import tools.fastlane.screengrab.Screengrab; -import tools.fastlane.screengrab.UiAutomatorScreenshotStrategy; -import tools.fastlane.screengrab.locale.LocaleTestRule; - -import static android.support.test.InstrumentationRegistry.getTargetContext; -import static tools.fastlane.screengrab.Screengrab.setDefaultScreenshotStrategy; - -public abstract class ScreenshotTest { - - @ClassRule - public static final LocaleTestRule localeTestRule = new LocaleTestRule(); - - protected static final String USERNAME = "Alice"; - protected static final String PASSWORD = "123456"; - - @Inject - protected AccountManager accountManager; - @Inject - protected LifecycleManager lifecycleManager; - - public ScreenshotTest() { - super(); - setDefaultScreenshotStrategy(new UiAutomatorScreenshotStrategy()); - BriarTestComponentApplication app = - (BriarTestComponentApplication) getTargetContext() - .getApplicationContext(); - inject((BriarUiTestComponent) app.getApplicationComponent()); - } - - protected abstract void inject(BriarUiTestComponent component); - - protected void screenshot(String name) { - try { - Screengrab.screenshot(name); - } catch (RuntimeException e) { - if (!e.getMessage().equals("Unable to capture screenshot.")) - throw e; - // The tests should still pass when run from AndroidStudio - // without manually granting permissions like fastlane does. - Log.w("Screengrab", "Permission to write screenshot is missing."); - } - } - - protected class CleanAccountTestRule - extends IntentsTestRule { - - public CleanAccountTestRule(Class activityClass) { - super(activityClass); - } - - @Override - protected void beforeActivityLaunched() { - super.beforeActivityLaunched(); - accountManager.deleteAccount(); - accountManager.createAccount(USERNAME, PASSWORD); - } - } - -} diff --git a/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/BriarUiTestComponent.java b/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/BriarUiTestComponent.java new file mode 100644 index 000000000..8aa02d42a --- /dev/null +++ b/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/BriarUiTestComponent.java @@ -0,0 +1,25 @@ +package org.briarproject.briar.android; + +import org.briarproject.bramble.BrambleAndroidModule; +import org.briarproject.bramble.BrambleCoreModule; +import org.briarproject.bramble.account.BriarAccountModule; +import org.briarproject.briar.BriarCoreModule; +import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest; + +import javax.inject.Singleton; + +import dagger.Component; + +@Singleton +@Component(modules = { + AppModule.class, + BriarCoreModule.class, + BrambleAndroidModule.class, + BriarAccountModule.class, + BrambleCoreModule.class +}) +public interface BriarUiTestComponent extends AndroidComponent { + + void inject(NavDrawerActivityTest test); + +} diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java b/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java similarity index 93% rename from briar-android/src/androidTest/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java rename to briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java index e92ae6693..5d55f2600 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java +++ b/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/navdrawer/NavDrawerActivityTest.java @@ -6,8 +6,8 @@ import android.view.Gravity; import org.briarproject.briar.R; import org.briarproject.briar.android.BriarUiTestComponent; +import org.briarproject.briar.android.UiTest; import org.briarproject.briar.android.settings.SettingsActivity; -import org.briarproject.briar.android.test.ScreenshotTest; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -23,7 +23,7 @@ import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; @RunWith(AndroidJUnit4.class) -public class NavDrawerActivityTest extends ScreenshotTest { +public class NavDrawerActivityTest extends UiTest { @Rule public CleanAccountTestRule testRule = diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/BriarUiTestComponent.java b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/BriarUiTestComponent.java similarity index 74% rename from briar-android/src/androidTest/java/org/briarproject/briar/android/BriarUiTestComponent.java rename to briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/BriarUiTestComponent.java index f00200ac5..860166099 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/BriarUiTestComponent.java +++ b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/BriarUiTestComponent.java @@ -4,8 +4,7 @@ import org.briarproject.bramble.BrambleAndroidModule; import org.briarproject.bramble.BrambleCoreModule; import org.briarproject.bramble.account.BriarAccountModule; import org.briarproject.briar.BriarCoreModule; -import org.briarproject.briar.android.login.SetupActivityScreenshotTest; -import org.briarproject.briar.android.navdrawer.NavDrawerActivityTest; +import org.briarproject.briar.android.contact.ConversationActivityScreenshotTest; import org.briarproject.briar.android.settings.SettingsActivityScreenshotTest; import javax.inject.Singleton; @@ -22,8 +21,9 @@ import dagger.Component; }) public interface BriarUiTestComponent extends AndroidComponent { - void inject(SetupActivityScreenshotTest test); - void inject(NavDrawerActivityTest test); + void inject(SetupDataTest test); + + void inject(ConversationActivityScreenshotTest test); void inject(SettingsActivityScreenshotTest test); } diff --git a/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/ScreenshotTest.java b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/ScreenshotTest.java new file mode 100644 index 000000000..2fcaf9c2e --- /dev/null +++ b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/ScreenshotTest.java @@ -0,0 +1,45 @@ +package org.briarproject.briar.android; + +import android.app.Activity; +import android.util.Log; + +import org.briarproject.bramble.api.plugin.ConnectionRegistry; +import org.briarproject.bramble.api.system.Clock; +import org.briarproject.briar.api.test.TestDataCreator; +import org.junit.ClassRule; + +import javax.inject.Inject; + +import tools.fastlane.screengrab.FalconScreenshotStrategy; +import tools.fastlane.screengrab.Screengrab; +import tools.fastlane.screengrab.locale.LocaleTestRule; + +public abstract class ScreenshotTest extends UiTest { + + @ClassRule + public static final LocaleTestRule localeTestRule = new LocaleTestRule(); + + @Inject + protected TestDataCreator testDataCreator; + @Inject + protected ConnectionRegistry connectionRegistry; + @Inject + protected Clock clock; + + protected void screenshot(String name, Activity activity) { + try { + Screengrab.screenshot(name, new FalconScreenshotStrategy(activity)); + } catch (RuntimeException e) { + if (!e.getMessage().equals("Unable to capture screenshot.")) + throw e; + // The tests should still pass when run from AndroidStudio + // without manually granting permissions like fastlane does. + Log.w("Screengrab", "Permission to write screenshot is missing."); + } + } + + protected long getMinutesAgo(int minutes) { + return clock.currentTimeMillis() - minutes * 60 * 1000; + } + +} diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/login/SetupActivityScreenshotTest.java b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/SetupDataTest.java similarity index 62% rename from briar-android/src/androidTest/java/org/briarproject/briar/android/login/SetupActivityScreenshotTest.java rename to briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/SetupDataTest.java index 475ae87a1..53b5d308f 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/login/SetupActivityScreenshotTest.java +++ b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/SetupDataTest.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android; import android.support.test.espresso.intent.rule.IntentsTestRule; import android.support.test.runner.AndroidJUnit4; @@ -6,9 +6,12 @@ import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject; import android.support.test.uiautomator.UiSelector; +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.db.DbException; import org.briarproject.briar.R; -import org.briarproject.briar.android.BriarUiTestComponent; -import org.briarproject.briar.android.test.ScreenshotTest; +import org.briarproject.briar.android.login.OpenDatabaseActivity; +import org.briarproject.briar.android.login.SetupActivity; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,14 +29,14 @@ import static android.support.test.espresso.matcher.ViewMatchers.isRoot; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; import static android.support.test.runner.lifecycle.Stage.PAUSED; -import static junit.framework.Assert.assertTrue; -import static org.briarproject.briar.android.test.ViewActions.waitForActivity; -import static org.briarproject.briar.android.test.ViewActions.waitUntilMatches; +import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; +import static org.briarproject.briar.android.ViewActions.waitForActivity; +import static org.briarproject.briar.android.ViewActions.waitUntilMatches; import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; - +import static org.junit.Assert.assertTrue; @RunWith(AndroidJUnit4.class) -public class SetupActivityScreenshotTest extends ScreenshotTest { +public class SetupDataTest extends ScreenshotTest { @Rule public IntentsTestRule testRule = @@ -61,7 +64,7 @@ public class SetupActivityScreenshotTest extends ScreenshotTest { onView(withId(R.id.nickname_entry)) .perform(waitUntilMatches(withText(USERNAME))); - screenshot("manual_create_account"); + screenshot("manual_create_account", testRule.getActivity()); onView(withId(R.id.next)) .check(matches(isDisplayed())) @@ -94,13 +97,54 @@ public class SetupActivityScreenshotTest extends ScreenshotTest { } // wait for OpenDatabaseActivity to show up - onView(withId(R.id.progress)) - .check(matches(isDisplayed())); onView(isRoot()) .perform(waitForActivity(testRule.getActivity(), PAUSED)); intended(hasComponent(OpenDatabaseActivity.class.getName())); assertTrue(accountManager.hasDatabaseKey()); + + lifecycleManager.waitForStartup(); + createTestData(); + + // close expiry warning + onView(withId(R.id.expiryWarning)) + .perform(waitUntilMatches(isDisplayed())); + onView(withId(R.id.expiryWarningClose)) + .check(matches(isDisplayed())); + onView(withId(R.id.expiryWarningClose)) + .perform(click()); + } + + private void createTestData() { + try { + createTestDataExceptions(); + } catch (DbException | FormatException e) { + throw new AssertionError(e); + } + } + + private void createTestDataExceptions() + throws DbException, FormatException { + String bobName = + getTargetContext().getString(R.string.screenshot_bob); + Contact bob = testDataCreator.addContact(bobName); + + String bobHi = getTargetContext() + .getString(R.string.screenshot_message_1); + long bobTime = getMinutesAgo(2); + testDataCreator.addPrivateMessage(bob, bobHi, bobTime, true); + + String aliceHi = getTargetContext() + .getString(R.string.screenshot_message_2); + long aliceTime = getMinutesAgo(1); + testDataCreator.addPrivateMessage(bob, aliceHi, aliceTime, false); + + String bobHi2 = getTargetContext() + .getString(R.string.screenshot_message_3); + long bobTime2 = getMinutesAgo(0); + testDataCreator.addPrivateMessage(bob, bobHi2, bobTime2, true); + + connectionRegistry.registerConnection(bob.getId(), ID, true); } } diff --git a/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/contact/ConversationActivityScreenshotTest.java b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/contact/ConversationActivityScreenshotTest.java new file mode 100644 index 000000000..e25951c33 --- /dev/null +++ b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/contact/ConversationActivityScreenshotTest.java @@ -0,0 +1,52 @@ +package org.briarproject.briar.android.contact; + +import android.content.Context; +import android.content.Intent; +import android.support.test.rule.ActivityTestRule; +import android.support.test.runner.AndroidJUnit4; + +import org.briarproject.briar.R; +import org.briarproject.briar.android.BriarUiTestComponent; +import org.briarproject.briar.android.ScreenshotTest; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static android.support.test.InstrumentationRegistry.getInstrumentation; +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withId; +import static android.support.test.espresso.matcher.ViewMatchers.withText; +import static org.briarproject.briar.android.ViewActions.waitUntilMatches; +import static org.briarproject.briar.android.contact.ConversationActivity.CONTACT_ID; +import static org.hamcrest.Matchers.allOf; + +@RunWith(AndroidJUnit4.class) +public class ConversationActivityScreenshotTest extends ScreenshotTest { + + @Rule + public ActivityTestRule testRule = + new ActivityTestRule<>(ConversationActivity.class, false, false); + + @Override + protected void inject(BriarUiTestComponent component) { + component.inject(this); + } + + @Test + public void messaging() throws Exception { + Context targetContext = getInstrumentation().getTargetContext(); + Intent intent = new Intent(targetContext, ConversationActivity.class); + intent.putExtra(CONTACT_ID, 1); + testRule.launchActivity(intent); + + onView(withId(R.id.conversationView)) + .perform(waitUntilMatches(allOf( + withText(R.string.screenshot_message_3), + isCompletelyDisplayed()) + )); + + screenshot("manual_messaging", testRule.getActivity()); + } + +} diff --git a/briar-android/src/androidTest/java/org/briarproject/briar/android/settings/SettingsActivityScreenshotTest.java b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/settings/SettingsActivityScreenshotTest.java similarity index 77% rename from briar-android/src/androidTest/java/org/briarproject/briar/android/settings/SettingsActivityScreenshotTest.java rename to briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/settings/SettingsActivityScreenshotTest.java index 34621c394..ceb1817f6 100644 --- a/briar-android/src/androidTest/java/org/briarproject/briar/android/settings/SettingsActivityScreenshotTest.java +++ b/briar-android/src/androidTestScreenshot/java/org/briarproject/briar/android/settings/SettingsActivityScreenshotTest.java @@ -2,14 +2,15 @@ package org.briarproject.briar.android.settings; import android.content.Intent; import android.support.test.espresso.contrib.DrawerActions; +import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.support.v7.widget.RecyclerView; import android.view.Gravity; import org.briarproject.briar.R; import org.briarproject.briar.android.BriarUiTestComponent; +import org.briarproject.briar.android.ScreenshotTest; import org.briarproject.briar.android.navdrawer.NavDrawerActivity; -import org.briarproject.briar.android.test.ScreenshotTest; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,15 +27,15 @@ import static android.support.test.espresso.matcher.ViewMatchers.withChild; import static android.support.test.espresso.matcher.ViewMatchers.withClassName; import static android.support.test.espresso.matcher.ViewMatchers.withId; import static android.support.test.espresso.matcher.ViewMatchers.withText; -import static org.briarproject.briar.android.test.ViewActions.waitUntilMatches; +import static org.briarproject.briar.android.ViewActions.waitUntilMatches; import static org.hamcrest.CoreMatchers.is; @RunWith(AndroidJUnit4.class) public class SettingsActivityScreenshotTest extends ScreenshotTest { @Rule - public CleanAccountTestRule testRule = - new CleanAccountTestRule<>(SettingsActivity.class); + public ActivityTestRule testRule = + new ActivityTestRule<>(SettingsActivity.class); @Override protected void inject(BriarUiTestComponent component) { @@ -46,7 +47,7 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest { onView(withText(R.string.settings_button)) .check(matches(isDisplayed())); - screenshot("manual_dark_theme_settings"); + screenshot("manual_dark_theme_settings", testRule.getActivity()); // switch to dark theme onView(withText(R.string.pref_theme_title)) @@ -56,10 +57,20 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest { .check(matches(isDisplayed())) .perform(click()); - // open nav drawer and remove expiry warning - openNavDrawer(true); + openNavDrawer(); - screenshot("manual_dark_theme_nav_drawer"); + screenshot("manual_dark_theme_nav_drawer", testRule.getActivity()); + + // switch to back to light theme + onView(withText(R.string.settings_button)) + .check(matches(isDisplayed())) + .perform(click()); + onView(withText(R.string.pref_theme_title)) + .check(matches(isDisplayed())) + .perform(click()); + onView(withText(R.string.pref_theme_light)) + .check(matches(isDisplayed())) + .perform(click()); } @Test @@ -83,12 +94,11 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest { .check(matches(isDisplayed())) .check(matches(isEnabled())); - screenshot("manual_app_lock"); + screenshot("manual_app_lock", testRule.getActivity()); - // no more expiry warning to remove, because sharedprefs cached? - openNavDrawer(false); + openNavDrawer(); - screenshot("manual_app_lock_nav_drawer"); + screenshot("manual_app_lock_nav_drawer", testRule.getActivity()); } @Test @@ -104,23 +114,15 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest { .check(matches(isDisplayed())) .perform(waitUntilMatches(isEnabled())); - screenshot("manual_tor_settings"); + screenshot("manual_tor_settings", testRule.getActivity()); } - private void openNavDrawer(boolean expiry) { + private void openNavDrawer() { // start main activity Intent i = new Intent(testRule.getActivity(), NavDrawerActivity.class); testRule.getActivity().startActivity(i); - // close expiry warning - if (expiry) { - onView(withId(R.id.expiryWarningClose)) - .check(matches(isDisplayed())); - onView(withId(R.id.expiryWarningClose)) - .perform(click()); - } - // open navigation drawer onView(withId(R.id.drawer_layout)) .check(matches(isClosed(Gravity.START))) diff --git a/briar-android/src/main/res/values/strings.xml b/briar-android/src/main/res/values/strings.xml index 2a63f683f..08afd7f57 100644 --- a/briar-android/src/main/res/values/strings.xml +++ b/briar-android/src/main/res/values/strings.xml @@ -480,4 +480,20 @@ Briar is locked Tap to unlock + + + + Alice + + Bob + + Carol + + Hi Bob! + + Hi Alice! Thanks for telling me about Briar! + + No problem, hope you like it 😀 + + diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle index 2a5727a16..0fa29a1f9 100644 --- a/briar-android/witness.gradle +++ b/briar-android/witness.gradle @@ -82,6 +82,7 @@ dependencyVerification { 'com.google.zxing:core:3.3.0:core-3.3.0.jar:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d', 'com.googlecode.json-simple:json-simple:1.1:json-simple-1.1.jar:2d9484f4c649f708f47f9a479465fc729770ee65617dca3011836602264f6439', 'com.ibm.icu:icu4j:53.1:icu4j-53.1.jar:e37a4467bac5cdeb02c5c4b8e5063d2f4e67b69e3c7df6d6b610f13185572bab', + 'com.jraska:falcon:1.0.4:falcon-1.0.4.aar:6114a48d8b3814f75fc69b5e84dc087c1254883874eae8a36bd778979800630a', 'com.squareup:javawriter:2.1.1:javawriter-2.1.1.jar:f699823d0081f69cbb676c1845ea222e0ada79bc88a53e5d22d8bd02d328f57e', 'com.squareup:javawriter:2.5.0:javawriter-2.5.0.jar:fcfb09fb0ea0aa97d3cfe7ea792398081348e468f126b3603cb3803f240197f0', 'com.sun.activation:javax.activation:1.2.0:javax.activation-1.2.0.jar:993302b16cd7056f21e779cc577d175a810bb4900ef73cd8fbf2b50f928ba9ce', @@ -171,7 +172,7 @@ dependencyVerification { 'org.robolectric:shadows-framework:3.8:shadows-framework-3.8.jar:83548db7249edf1af87e1a1f4d8f4eec3e85d6220161da601e6f6398476911b2', 'org.robolectric:shadows-support-v4:3.3.2:shadows-support-v4-3.3.2.jar:6f689264738266e70fe08db7c04b7b5a75155994f4e3f7f311960d90486bf005', 'org.robolectric:utils:3.8:utils-3.8.jar:e945d04d40e37554e02d4be1bc3abf9bede45375c843aa36d10ccb6b63edbf34', - 'tools.fastlane:screengrab:1.1.0:screengrab-1.1.0.aar:03ce3868ee8a0082d14e7a1de0999f91531c0cc794392688beb08ee9bc4495fd', + 'tools.fastlane:screengrab:1.2.0:screengrab-1.2.0.aar:af4ee23bb06f94404d3ab18e2ea69db8265539fc8da29f9ee45b7e472684ba83', 'uk.co.samuelwall:material-tap-target-prompt:2.8.0:material-tap-target-prompt-2.8.0.aar:ac70770c05bbc4675a1d5712c0e53d46ee4fa961b74947589fce50d8003065ec', 'xmlpull:xmlpull:1.1.3.1:xmlpull-1.1.3.1.jar:34e08ee62116071cbb69c0ed70d15a7a5b208d62798c59f2120bb8929324cb63', 'xpp3:xpp3_min:1.1.4c:xpp3_min-1.1.4c.jar:bfc90e9e32d0eab1f397fb974b5f150a815188382ac41f372a7149d5bc178008', diff --git a/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java b/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java index fc210e583..fe1178307 100644 --- a/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java +++ b/briar-api/src/main/java/org/briarproject/briar/api/test/TestDataCreator.java @@ -1,12 +1,16 @@ package org.briarproject.briar.api.test; +import org.briarproject.bramble.api.FormatException; +import org.briarproject.bramble.api.contact.Contact; +import org.briarproject.bramble.api.db.DbException; +import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @NotNullByDefault public interface TestDataCreator { /** - * Create fake test data on the DatabaseExecutor + * Create fake test data on the IoExecutor * * @param numContacts Number of contacts to create. Must be >= 1 * @param numPrivateMsgs Number of private messages to create for each @@ -18,4 +22,11 @@ public interface TestDataCreator { void createTestData(int numContacts, int numPrivateMsgs, int numBlogPosts, int numForums, int numForumPosts); + @IoExecutor + Contact addContact(String name) throws DbException; + + @IoExecutor + void addPrivateMessage(Contact contact, String body, long time, + boolean local) throws DbException, FormatException; + } diff --git a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java index fd66761ea..6b2dbc243 100644 --- a/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java +++ b/briar-core/src/main/java/org/briarproject/briar/test/TestDataCreatorImpl.java @@ -13,9 +13,11 @@ import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.identity.AuthorFactory; +import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.LanTcpConstants; import org.briarproject.bramble.api.plugin.TorConstants; @@ -59,6 +61,7 @@ import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.briar.test.TestData.AUTHOR_NAMES; import static org.briarproject.briar.test.TestData.GROUP_NAMES; +@NotNullByDefault public class TestDataCreatorImpl implements TestDataCreator { private final Logger LOG = @@ -146,17 +149,17 @@ public class TestDataCreatorImpl implements TestDataCreator { List contacts = new ArrayList<>(numContacts); LocalAuthor localAuthor = identityManager.getLocalAuthor(); for (int i = 0; i < numContacts; i++) { - Contact contact = addRandomContact(localAuthor); + LocalAuthor author = getRandomAuthor(); + Contact contact = addContact(localAuthor.getId(), author); contacts.add(contact); } return contacts; } - private Contact addRandomContact(LocalAuthor localAuthor) + private Contact addContact(AuthorId localAuthorId, LocalAuthor author) throws DbException { // prepare to add contact - LocalAuthor author = getRandomAuthor(); SecretKey secretKey = getSecretKey(); long timestamp = clock.currentTimeMillis(); boolean verified = random.nextBoolean(); @@ -169,7 +172,7 @@ public class TestDataCreatorImpl implements TestDataCreator { Transaction txn = db.startTransaction(false); try { ContactId contactId = contactManager - .addContact(txn, author, localAuthor.getId(), secretKey, + .addContact(txn, author, localAuthorId, secretKey, timestamp, true, verified, true); transportPropertyManager.addRemoteProperties(txn, contactId, props); contact = db.getContact(txn, contactId); @@ -186,14 +189,23 @@ public class TestDataCreatorImpl implements TestDataCreator { return contact; } - private LocalAuthor getRandomAuthor() { - int i = random.nextInt(AUTHOR_NAMES.length); - String authorName = AUTHOR_NAMES[i]; + @Override + public Contact addContact(String name) throws DbException { + LocalAuthor localAuthor = identityManager.getLocalAuthor(); + return addContact(localAuthor.getId(), getAuthor(name)); + } + + private LocalAuthor getAuthor(String name) { KeyPair keyPair = cryptoComponent.generateSignatureKeyPair(); byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] privateKey = keyPair.getPrivate().getEncoded(); - return authorFactory.createLocalAuthor(authorName, publicKey, - privateKey); + return authorFactory.createLocalAuthor(name, publicKey, privateKey); + } + + private LocalAuthor getRandomAuthor() { + int i = random.nextInt(AUTHOR_NAMES.length); + String authorName = AUTHOR_NAMES[i]; + return getAuthor(authorName); } private SecretKey getSecretKey() { @@ -285,7 +297,7 @@ public class TestDataCreatorImpl implements TestDataCreator { Group group = messagingManager.getContactGroup(contact); for (int i = 0; i < numPrivateMsgs; i++) { try { - createPrivateMessage(group.getId(), i); + createRandomPrivateMessage(group.getId(), i); } catch (FormatException e) { throw new RuntimeException(e); } @@ -297,14 +309,18 @@ public class TestDataCreatorImpl implements TestDataCreator { } } - private void createPrivateMessage(GroupId groupId, int num) + private void createRandomPrivateMessage(GroupId groupId, int num) throws DbException, FormatException { long timestamp = clock.currentTimeMillis() - num * 60 * 1000; String body = getRandomText(); + boolean local = random.nextBoolean(); + createPrivateMessage(groupId, body, timestamp, local); + } + + private void createPrivateMessage(GroupId groupId, String body, + long timestamp, boolean local) throws DbException, FormatException { PrivateMessage m = privateMessageFactory .createPrivateMessage(groupId, timestamp, body); - - boolean local = random.nextBoolean(); BdfDictionary meta = new BdfDictionary(); meta.put("timestamp", timestamp); meta.put("local", local); @@ -321,6 +337,13 @@ public class TestDataCreatorImpl implements TestDataCreator { } } + @Override + public void addPrivateMessage(Contact contact, String body, long time, + boolean local) throws DbException, FormatException { + Group group = messagingManager.getContactGroup(contact); + createPrivateMessage(group.getId(), body, time, local); + } + private void createBlogPosts(List contacts, int numBlogPosts) throws DbException { for (int i = 0; i < numBlogPosts; i++) {