designer/testSrc/com/android/tools/idea/uibuilder/palette/DependencyManagerTest.kt (195 lines of code) (raw):

/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.tools.idea.uibuilder.palette import com.android.AndroidXConstants.CARD_VIEW import com.android.AndroidXConstants.FLOATING_ACTION_BUTTON import com.android.AndroidXConstants.RECYCLER_VIEW import com.android.SdkConstants.TEXT_VIEW import com.android.tools.idea.projectsystem.PLATFORM_SUPPORT_LIBS import com.android.tools.idea.projectsystem.PROJECT_SYSTEM_SYNC_TOPIC import com.android.tools.idea.projectsystem.ProjectSystemSyncManager.SyncResult import com.android.tools.idea.projectsystem.TestProjectSystem import com.android.tools.idea.testing.AndroidProjectRule import com.android.tools.idea.uibuilder.type.LayoutFileType import com.google.common.truth.Truth.assertThat import com.intellij.ide.impl.OpenProjectTask import com.intellij.openapi.Disposable import com.intellij.openapi.command.WriteCommandAction import com.intellij.openapi.module.JavaModuleType.JAVA_MODULE_ENTITY_TYPE_ID_NAME import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.ex.ProjectManagerEx import com.intellij.openapi.ui.Messages import com.intellij.openapi.ui.TestDialog import com.intellij.openapi.ui.TestDialogManager import com.intellij.openapi.util.Computable import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Ref import com.intellij.testFramework.runInEdtAndWait import com.intellij.util.ui.UIUtil import org.jetbrains.android.facet.AndroidFacet import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.rules.RuleChain import org.junit.rules.TestName import org.mockito.Mockito.mock import java.nio.file.Files.createTempDirectory import java.util.ArrayDeque import java.util.concurrent.Future class DependencyManagerTest { private var panel: PalettePanel? = null private var palette: Palette? = null private var disposable: Disposable? = null private var dependencyManager: DependencyManager? = null private var dependencyUpdateCount = 0 private val syncListeners = ArrayDeque<Future<*>>() private val dialogMessages = mutableListOf<String>() private val projectRule = AndroidProjectRule.onDisk() private val watcher = TestName() private lateinit var testProjectSystem: TestProjectSystem @get:Rule val rule = RuleChain.outerRule(projectRule).around(watcher)!! @Before fun setUp() { testProjectSystem = TestProjectSystem(projectRule.project, availableDependencies = PLATFORM_SUPPORT_LIBS) testProjectSystem.useAndroidX = true runInEdtAndWait { testProjectSystem.useInTests() } panel = mock(PalettePanel::class.java) palette = NlPaletteModel.get(AndroidFacet.getInstance(projectRule.module)!!).getPalette(LayoutFileType) disposable = Disposer.newDisposable() Disposer.register(projectRule.testRootDisposable, disposable!!) dependencyManager = DependencyManager(projectRule.project) dependencyManager?.setSyncTopicListener { syncListeners.add(it) } if (watcher.methodName != "testNoNotificationOnProjectSyncBeforeSetPalette") { dependencyManager!!.setPalette(palette!!, projectRule.module) waitAndDispatchAll() } Disposer.register(disposable!!, dependencyManager!!) dependencyManager!!.addDependencyChangeListener { dependencyUpdateCount++ } TestDialogManager.setTestDialog { message: String -> dialogMessages.add(message.trim()) // Remove line break in the end of the message. Messages.OK } } @After fun tearDown() { TestDialogManager.setTestDialog(TestDialog.DEFAULT) // Null out all fields, since otherwise they're retained for the lifetime of the suite (which // can be long if e.g. you're running many // tests through IJ) Disposer.dispose(disposable!!) palette = null panel = null dependencyManager = null disposable = null } @Test fun testNeedsLibraryLoad() { assertThat(dependencyManager!!.needsLibraryLoad(findItem(TEXT_VIEW))).isFalse() assertThat(dependencyManager!!.needsLibraryLoad(findItem(FLOATING_ACTION_BUTTON.defaultName()))) .isTrue() } @Test fun testEnsureLibraryIsIncluded() { val (floatingActionButton, recyclerView, cardView) = listOf( FLOATING_ACTION_BUTTON.defaultName(), RECYCLER_VIEW.defaultName(), CARD_VIEW.defaultName(), ) .map(this::findItem) assertThat(dependencyManager!!.needsLibraryLoad(floatingActionButton)).isTrue() assertThat(dependencyManager!!.needsLibraryLoad(recyclerView)).isTrue() assertThat(dependencyManager!!.needsLibraryLoad(cardView)).isTrue() runInEdtAndWait { dependencyManager!!.ensureLibraryIsIncluded(floatingActionButton) waitAndDispatchAll() dependencyManager!!.ensureLibraryIsIncluded(cardView) waitAndDispatchAll() } assertThat(dependencyManager!!.needsLibraryLoad(floatingActionButton)).isFalse() assertThat(dependencyManager!!.needsLibraryLoad(recyclerView)).isTrue() assertThat(dependencyManager!!.needsLibraryLoad(cardView)).isFalse() } @Test fun testRegisterDependencyUpdates() { simulateProjectSync() assertThat(dependencyUpdateCount).isEqualTo(0) runInEdtAndWait { dependencyManager!!.ensureLibraryIsIncluded(findItem(FLOATING_ACTION_BUTTON.defaultName())) waitAndDispatchAll() } assertThat(dependencyUpdateCount).isEqualTo(1) } @Test fun testDisposeStopsProjectSyncListening() { Disposer.dispose(disposable!!) runInEdtAndWait { dependencyManager!!.ensureLibraryIsIncluded(findItem(FLOATING_ACTION_BUTTON.defaultName())) waitAndDispatchAll() } assertThat(dependencyUpdateCount).isEqualTo(0) } @Test fun testAndroidxDependencies() { assertThat(dependencyManager!!.useAndroidXDependencies()).isTrue() testProjectSystem.useAndroidX = false simulateProjectSync() assertThat(dependencyManager!!.useAndroidXDependencies()).isFalse() assertThat(dependencyUpdateCount).isEqualTo(1) testProjectSystem.useAndroidX = true simulateProjectSync() assertThat(dependencyManager!!.useAndroidXDependencies()).isTrue() assertThat(dependencyUpdateCount).isEqualTo(2) } @Test fun testNoNotificationOnProjectSyncBeforeSetPalette() { dependencyManager!!.setNotifyAlways(true) simulateProjectSync() assertThat(dependencyUpdateCount).isEqualTo(0) } @Test fun testSetPaletteWithDisposedProject() { val foo = createTempDirectory("foo") val bar = createTempDirectory("bar") val tempProject = ProjectManagerEx.getInstanceEx().newProject(foo, OpenProjectTask(isNewProject = true))!! val localDependencyManager: DependencyManager try { val tempModule = WriteCommandAction.runWriteCommandAction( tempProject, Computable<Module> { ModuleManager.getInstance(tempProject).newModule(bar, JAVA_MODULE_ENTITY_TYPE_ID_NAME) }, ) localDependencyManager = DependencyManager(tempProject) Disposer.register(tempProject, localDependencyManager) localDependencyManager.setPalette(palette!!, tempModule) } finally { WriteCommandAction.runWriteCommandAction(tempProject) { Disposer.dispose(tempProject) } } } private fun simulateProjectSync() { projectRule.project.messageBus .syncPublisher(PROJECT_SYSTEM_SYNC_TOPIC) .syncEnded(SyncResult.SUCCESS) waitAndDispatchAll() } private fun waitAndDispatchAll() { while (syncListeners.isNotEmpty()) { syncListeners.remove().get() } runInEdtAndWait { UIUtil.dispatchAllInvocationEvents() } } private fun findItem(tagName: String): Palette.Item { val found = Ref<Palette.Item>() palette!!.accept { item -> if (item.tagName == tagName) { found.set(item) } } if (found.isNull) { throw RuntimeException("The item: $tagName was not found on the palette.") } return found.get() } }