diff --git a/briar-desktop/build.gradle.kts b/briar-desktop/build.gradle.kts index de8841fed03e9cf2be4d84957c8ff7d64dcb16ae..bf12f8ba11190e06b557804fcb17b12c081ed166 100644 --- a/briar-desktop/build.gradle.kts +++ b/briar-desktop/build.gradle.kts @@ -139,6 +139,28 @@ tasks.test { useJUnit() } +// see https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests +sourceSets.create("automatedScreenshots") { + kotlin.srcDir("$projectDir/src/automatedScreenshots/kotlin") + resources.srcDir("$projectDir/src/automatedScreenshots/resources") + compileClasspath += sourceSets.main.get().output + sourceSets.test.get().output + runtimeClasspath += sourceSets.main.get().output + sourceSets.test.get().output +} + +configurations["automatedScreenshotsImplementation"].extendsFrom(configurations.testImplementation.get()) +configurations["automatedScreenshotsRuntimeOnly"].extendsFrom(configurations.testRuntimeOnly.get()) + +task<Test>("automatedScreenshots") { + testClassesDirs = sourceSets["automatedScreenshots"].output.classesDirs + classpath = sourceSets["automatedScreenshots"].runtimeClasspath +} + +// makes `internal` visible in "automatedScreenshot" +// see https://kotlinlang.org/docs/gradle-configure-project.html#associate-compiler-tasks +kotlin.target.compilations.named("automatedScreenshots") { + associateWith(kotlin.target.compilations.getByName("test")) +} + tasks.withType<KotlinCompile> { kotlinOptions.jvmTarget = "11" kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" diff --git a/briar-desktop/src/automatedScreenshots/kotlin/org/briarproject/briar/desktop/ScreenshotTest.kt b/briar-desktop/src/automatedScreenshots/kotlin/org/briarproject/briar/desktop/ScreenshotTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..a37d91cba78389b8ab18df36dab86aae6c6a55f9 --- /dev/null +++ b/briar-desktop/src/automatedScreenshots/kotlin/org/briarproject/briar/desktop/ScreenshotTest.kt @@ -0,0 +1,126 @@ +/* + * Briar Desktop + * Copyright (C) 2021-2022 The Briar Project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package org.briarproject.briar.desktop + +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.awt.ComposeWindow +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.runDesktopComposeUiTest +import androidx.compose.ui.window.FrameWindowScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.briarproject.bramble.BrambleCoreEagerSingletons +import org.briarproject.bramble.api.contact.event.ContactAddedEvent +import org.briarproject.bramble.api.plugin.LanTcpConstants +import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT +import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT +import org.briarproject.briar.BriarCoreEagerSingletons +import org.briarproject.briar.desktop.TestUtils.getDataDir +import org.briarproject.briar.desktop.contact.ContactListViewModel +import org.briarproject.briar.desktop.ui.LocalWindowFocusState +import org.briarproject.briar.desktop.ui.LocalWindowScope +import org.briarproject.briar.desktop.ui.WindowFocusState +import org.jetbrains.annotations.NonNls +import org.jetbrains.skia.Image +import org.junit.Test +import java.io.FileOutputStream + +@OptIn(ExperimentalTestApi::class) +class ScreenshotTest { + @Test + fun makeScreenshot() = runDesktopComposeUiTest(700, 700) { + // TODO: unify with interactive tests + val dataDir = getDataDir() + val app = + DaggerBriarDesktopTestApp.builder().desktopCoreModule( + DesktopCoreModule(dataDir, DEFAULT_SOCKS_PORT, DEFAULT_CONTROL_PORT) + ).build() + // We need to load the eager singletons directly after making the + // dependency graphs + BrambleCoreEagerSingletons.Helper.injectEagerSingletons(app) + BriarCoreEagerSingletons.Helper.injectEagerSingletons(app) + + windowScope = object : FrameWindowScope { + override val window: ComposeWindow get() = TODO() + } + windowFocusState = WindowFocusState().apply { focused = true } + + setContent { + CompositionLocalProvider( + LocalWindowScope provides windowScope, + LocalWindowFocusState provides windowFocusState, + ) { + app.getBriarUi().content() + } + } + + captureToImage().save("before-click.png") + onNodeWithTag("close_expiration").performClick() + captureToImage().save("after-click.png") + + // TODO: unify with interactive tests + val lifecycleManager = app.getLifecycleManager() + val accountManager = app.getAccountManager() + + @NonNls + val password = "verySecret123!" + accountManager.createAccount("alice", password) + + val dbKey = accountManager.databaseKey ?: throw AssertionError() + lifecycleManager.startServices(dbKey) + lifecycleManager.waitForStartup() + + runBlocking { + delay(1000) + + captureToImage().save("after-login.png") + + app.getDeterministicTestDataCreator().createTestData(5, 20, 50, 10, 20) + app.getContactManager().addPendingContact("briar://aatkjq4seoualafpwh4cfckdzr4vpr4slk3bbvpxklf7y7lv4ajw6", "Faythe") + + app.getEventBus().addListener { e -> + if (e is ContactAddedEvent) { + if (app.getContactManager().getContact(e.contactId).author.name in listOf("Bob", "Chuck")) // NON-NLS + app.getIoExecutor().execute { + app.getConnectionRegistry().registerIncomingConnection(e.contactId, LanTcpConstants.ID) {} + } + } + } + + delay(1000) + + val viewModel = app.getViewModelProvider().get(ContactListViewModel::class) + viewModel.selectContact(viewModel.contactList.value[1]) + + delay(1000) + + captureToImage().save("after-contacts.png") + } + } +} + +private fun Image.save(file: String) { + encodeToData()?.bytes?.let { bytes -> + FileOutputStream(file).use { out -> + out.write(bytes) + } + } +} diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt index 917d5f25595693a5be333b535c7b0e229208248f..c944a6f4776a6f7110921d99e4be89e4daeef546 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/ui/BriarUi.kt @@ -142,12 +142,6 @@ constructor( var lastNotificationPrivateMessage = 0L var lastNotificationForum = 0L - val eventListener = EventListener { e -> - when (e) { - is LifecycleEvent -> - if (e.lifecycleState == RUNNING) screenState = MAIN - } - } val focusListener = object : WindowFocusListener { override fun windowGainedFocus(e: WindowEvent?) { focusState.focused = true @@ -194,13 +188,11 @@ constructor( visualNotificationProvider.init() soundNotificationProvider.init() - eventBus.addListener(eventListener) window.addWindowFocusListener(focusListener) messageCounter.addListener(messageCounterListener) onDispose { messageCounter.removeListener(messageCounterListener) - eventBus.removeListener(eventListener) window.removeWindowFocusListener(focusListener) visualNotificationProvider.uninit() soundNotificationProvider.uninit() @@ -228,6 +220,21 @@ constructor( @OptIn(ExperimentalFoundationApi::class) @Composable override fun content() { + DisposableEffect(Unit) { + val eventListener = EventListener { e -> + when (e) { + is LifecycleEvent -> { + if (e.lifecycleState == RUNNING) screenState = MAIN + } + } + } + eventBus.addListener(eventListener) + + onDispose { + eventBus.removeListener(eventListener) + } + } + CompositionLocalProvider( LocalViewModelProvider provides viewModelProvider, LocalConfiguration provides configuration, diff --git a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/ViewModelProvider.kt b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/ViewModelProvider.kt index 5575954f914f05f1829cba6af13e22a213043fca..7f77094ea82fba16e72bc287120ccce4733b9b7f 100644 --- a/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/ViewModelProvider.kt +++ b/briar-desktop/src/main/kotlin/org/briarproject/briar/desktop/viewmodel/ViewModelProvider.kt @@ -24,8 +24,10 @@ originally licensed under the Apache License, Version 2.0 package org.briarproject.briar.desktop.viewmodel import javax.inject.Inject +import javax.inject.Singleton import kotlin.reflect.KClass +@Singleton class ViewModelProvider @Inject constructor( diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/BriarDesktopTestApp.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/BriarDesktopTestApp.kt index 9557b64d133914253ab90f17f091a6fab3519666..f3888c693b923732f78ed31aa31ee7915233a0d7 100644 --- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/BriarDesktopTestApp.kt +++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/BriarDesktopTestApp.kt @@ -40,6 +40,7 @@ import org.briarproject.briar.api.test.TestDataCreator import org.briarproject.briar.desktop.testdata.DeterministicTestDataCreator import org.briarproject.briar.desktop.threading.BriarExecutors import org.briarproject.briar.desktop.ui.BriarUi +import org.briarproject.briar.desktop.viewmodel.ViewModelProvider import java.util.concurrent.Executor import javax.inject.Singleton @@ -87,4 +88,6 @@ internal interface BriarDesktopTestApp : BrambleCoreEagerSingletons, BriarCoreEa fun getForumManager(): ForumManager fun getForumSharingManager(): ForumSharingManager + + fun getViewModelProvider(): ViewModelProvider } diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt index 7b50b7493bbf9ee3e34fdcd8324403de2ab237bf..c39d64c9bf8990176b3a3b9569f27e2b42fbeb08 100644 --- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt +++ b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/RunWithTemporaryAccount.kt @@ -21,7 +21,6 @@ package org.briarproject.briar.desktop import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.window.application import mu.KotlinLogging import org.briarproject.bramble.BrambleCoreEagerSingletons @@ -48,7 +47,6 @@ internal class RunWithTemporaryAccount( private val LOG = KotlinLogging.logger {} } - @OptIn(ExperimentalComposeUiApi::class) fun run() { LogUtils.setupLogging(ALL) diff --git a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/ScreenshotTest.kt b/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/ScreenshotTest.kt deleted file mode 100644 index b24d4cb7fe4081fec4b86daebbc60981e49d5f66..0000000000000000000000000000000000000000 --- a/briar-desktop/src/test/kotlin/org/briarproject/briar/desktop/ScreenshotTest.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Briar Desktop - * Copyright (C) 2021-2022 The Briar Project - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero 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 Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -package org.briarproject.briar.desktop - -import androidx.compose.ui.test.ExperimentalTestApi -import androidx.compose.ui.test.onNodeWithTag -import androidx.compose.ui.test.performClick -import androidx.compose.ui.test.runDesktopComposeUiTest -import org.briarproject.bramble.BrambleCoreEagerSingletons -import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_CONTROL_PORT -import org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_SOCKS_PORT -import org.briarproject.briar.BriarCoreEagerSingletons -import org.briarproject.briar.desktop.TestUtils.getDataDir -import org.jetbrains.skia.Image -import org.junit.Test -import java.io.FileOutputStream - -@OptIn(ExperimentalTestApi::class) -class ScreenshotTest { - @Test - fun makeScreenshot() = runDesktopComposeUiTest(700, 500) { - val dataDir = getDataDir() - val app = - DaggerBriarDesktopTestApp.builder().desktopCoreModule( - DesktopCoreModule(dataDir, DEFAULT_SOCKS_PORT, DEFAULT_CONTROL_PORT) - ).build() - // We need to load the eager singletons directly after making the - // dependency graphs - BrambleCoreEagerSingletons.Helper.injectEagerSingletons(app) - BriarCoreEagerSingletons.Helper.injectEagerSingletons(app) - - val ui = app.getBriarUi() - - setContent { - ui.content() - } - captureToImage().save("before-click.png") - onNodeWithTag("close_expiration").performClick() - captureToImage().save("after-click.png") - } -} - -private fun Image.save(file: String) { - encodeToData()?.bytes?.let { bytes -> - FileOutputStream(file).use { out -> - out.write(bytes) - } - } -}