| /* |
| * Copyright (C) 2013 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.rendering; |
| |
| import com.android.SdkConstants; |
| import com.android.ide.common.rendering.api.*; |
| import com.android.ide.common.res2.DataBindingResourceType; |
| import com.android.ide.common.res2.ResourceFile; |
| import com.android.ide.common.res2.ResourceItem; |
| import com.android.resources.Density; |
| import com.android.resources.ResourceFolderType; |
| import com.android.resources.ResourceType; |
| import com.android.tools.idea.databinding.DataBindingUtil; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.editor.Document; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.psi.PsiDirectory; |
| import com.intellij.psi.PsiDocumentManager; |
| import com.intellij.psi.PsiFile; |
| import com.intellij.psi.PsiManager; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.xml.XmlFile; |
| import com.intellij.psi.xml.XmlTag; |
| import com.intellij.util.ui.UIUtil; |
| import org.jetbrains.android.AndroidTestCase; |
| import org.jetbrains.android.facet.AndroidFacet; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.*; |
| |
| import static com.android.SdkConstants.*; |
| import static com.android.tools.idea.rendering.ResourceFolderRepository.ourFullRescans; |
| |
| /** |
| * TODO: Add XmlTags with Psi events to check childAdded etc working correctly! Currently they mostly seem to generate big rescans. |
| * TODO: Test moving from one resource folder to another; should be simulated as an add in one folder and a remove in another; |
| * check that in the ModuleResourceRepository test! |
| * TODO: Test that adding and removing characters inside a {@code <string>} element does not cause a full rescan |
| */ |
| @SuppressWarnings({"UnusedDeclaration", "SpellCheckingInspection"}) |
| public class ResourceFolderRepositoryTest extends AndroidTestCase { |
| private static final String LAYOUT1 = "resourceRepository/layout.xml"; |
| private static final String LAYOUT2 = "resourceRepository/layout2.xml"; |
| private static final String LAYOUT_ID_SCAN = "resourceRepository/layout_for_id_scan.xml"; |
| private static final String LAYOUT_WITH_DATA_BINDING = "resourceRepository/layout_with_data_binding.xml"; |
| private static final String VALUES1 = "resourceRepository/values.xml"; |
| private static final String VALUES_EMPTY = "resourceRepository/empty.xml"; |
| private static final String XLIFF = "resourceRepository/xliff.xml"; |
| private static final String STRINGS = "resourceRepository/strings.xml"; |
| private static final String DRAWABLE = "resourceRepository/logo.png"; |
| |
| @Override |
| public void tearDown() throws Exception { |
| ResourceFolderRegistry.reset(); |
| super.tearDown(); |
| } |
| |
| @Override |
| public void setUp() throws Exception { |
| super.setUp(); |
| ResourceFolderRegistry.reset(); |
| } |
| |
| private static void resetScanCounter() { |
| ourFullRescans = 0; |
| } |
| |
| private static void ensureIncremental() { |
| assertEquals(0, ourFullRescans); |
| } |
| |
| private static void ensureSingleScan() { |
| assertEquals(1, ourFullRescans); |
| } |
| |
| private ResourceFolderRepository createRepository() { |
| List<VirtualFile> resourceDirectories = myFacet.getAllResourceDirectories(); |
| assertNotNull(resourceDirectories); |
| assertSize(1, resourceDirectories); |
| VirtualFile dir = resourceDirectories.get(0); |
| return ResourceFolderRegistry.get(myFacet, dir); |
| } |
| |
| public void testComputeResourceStrings() throws Exception { |
| // Tests the handling of markup to raw strings |
| // For example, for this strings definition |
| // <string name="title_template_step">Step <xliff:g id="step_number">%1$d</xliff:g>: Lorem Ipsum</string> |
| // the resource value should be |
| // Step %1$d: Lorem Ipsum |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| List<ResourceItem> labelList = resources.getResourceItem(ResourceType.STRING, "title_template_step"); |
| assertNotNull(labelList); |
| assertEquals(1, labelList.size()); |
| ResourceItem label = labelList.get(0); |
| ResourceValue resourceValue = label.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Step ${step_number}: Lorem Ipsum", resourceValue.getValue()); // In the file, there's whitespace unlike example above |
| |
| // Test unicode escape handling: <string name="ellipsis">Here it is: \u2026!</string> |
| labelList = resources.getResourceItem(ResourceType.STRING, "ellipsis"); |
| assertNotNull(labelList); |
| assertEquals(1, labelList.size()); |
| label = labelList.get(0); |
| resourceValue = label.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Here it is: \u2026!", resourceValue.getValue()); |
| |
| // Make sure we pick up id's defined using types |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "action_next")); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "action_next2")); |
| } |
| |
| @SuppressWarnings("ConstantConditions") |
| public void testXliff() throws Exception { |
| // Tests the handling of xliff markup |
| VirtualFile file1 = myFixture.copyFileToProject(XLIFF, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertEquals("Share your score of (1337) with (Bluetooth)!", |
| resources.getResourceItem(ResourceType.STRING, "share_with_application").get(0).getResourceValue(false).getValue()); |
| assertEquals("Call ${name}", |
| resources.getResourceItem(ResourceType.STRING, "description_call").get(0).getResourceValue(false).getValue()); |
| assertEquals("(42) mins (28) secs", |
| resources.getResourceItem(ResourceType.STRING, "callDetailsDurationFormat").get(0).getResourceValue(false).getValue()); |
| assertEquals("${number_of_sessions} sessions removed from your schedule", |
| resources.getResourceItem(ResourceType.STRING, "other").get(0).getResourceValue(false).getValue()); |
| } |
| |
| public void testInitialCreate() throws Exception { |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout2.xml"); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(2, layouts.size()); |
| |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout1")); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout2")); |
| } |
| |
| public void testAddFile() throws Exception { |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout2.xml"); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(2, layouts.size()); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout1")); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout2")); |
| |
| long generation = resources.getModificationCount(); |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout2.xml"); |
| assertEquals(generation, resources.getModificationCount()); // no changes in file: no new generation |
| |
| generation = resources.getModificationCount(); |
| VirtualFile file3 = myFixture.copyFileToProject(LAYOUT1, "res/layout-xlarge-land/layout3.xml"); |
| PsiFile psiFile3 = PsiManager.getInstance(getProject()).findFile(file3); |
| assertNotNull(psiFile3); |
| assertTrue(resources.getModificationCount() > generation); |
| |
| layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(3, layouts.size()); |
| } |
| |
| public void testAddUnrelatedFile() throws Exception { |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout2.xml"); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(2, layouts.size()); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout1")); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout2")); |
| |
| long generation = resources.getModificationCount(); |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout2.unrelated"); |
| assertEquals(generation, resources.getModificationCount()); // no changes in file: no new generation |
| assertEquals(2, layouts.size()); |
| |
| myFixture.copyFileToProject(LAYOUT1, "src/layout/layout2.xml"); // not a resource folder |
| assertEquals(generation, resources.getModificationCount()); // no changes in file: no new generation |
| assertEquals(2, layouts.size()); |
| } |
| |
| public void testDeleteResourceFile() throws Exception { |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| Collection<String> drawables = resources.getItemsOfType(ResourceType.DRAWABLE); |
| assertEquals(drawables.toString(), 0, drawables.size()); |
| long generation = resources.getModificationCount(); |
| VirtualFile file4 = myFixture.copyFileToProject(LAYOUT1, "res/drawable-mdpi/foo.png"); |
| final PsiFile psiFile4 = PsiManager.getInstance(getProject()).findFile(file4); |
| assertNotNull(psiFile4); |
| assertTrue(resources.getModificationCount() > generation); |
| |
| // Delete a file and make sure the item is removed from the repository (and modification count bumped) |
| drawables = resources.getItemsOfType(ResourceType.DRAWABLE); |
| assertEquals(1, drawables.size()); |
| generation = resources.getModificationCount(); |
| assertEquals("foo", drawables.iterator().next()); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| psiFile4.delete(); |
| } |
| }); |
| drawables = resources.getItemsOfType(ResourceType.DRAWABLE); |
| assertEquals(0, drawables.size()); |
| assertTrue(resources.getModificationCount() > generation); |
| } |
| |
| public void testDeleteResourceDirectory() throws Exception { |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout2.xml"); |
| final VirtualFile file3 = myFixture.copyFileToProject(LAYOUT1, "res/layout-xlarge-land/layout3.xml"); |
| PsiFile psiFile3 = PsiManager.getInstance(getProject()).findFile(file3); |
| assertNotNull(psiFile3); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| // Try deleting a whole resource directory and ensure we remove the files within |
| long generation = resources.getModificationCount(); |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(3, layouts.size()); |
| final PsiDirectory directory = psiFile3.getContainingDirectory(); |
| assertNotNull(directory); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| directory.delete(); |
| } |
| }); |
| layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(2, layouts.size()); |
| assertTrue(resources.getModificationCount() > generation); |
| } |
| |
| public void testRenameLayoutFile() throws Exception { |
| final VirtualFile file2 = myFixture.copyFileToProject(LAYOUT1, "res/layout/layout2.xml"); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| // Check renames |
| // rename layout file |
| long generation = resources.getModificationCount(); |
| assertTrue(resources.hasResourceItem(ResourceType.LAYOUT, "layout2")); |
| assertFalse(resources.hasResourceItem(ResourceType.LAYOUT, "layout2b")); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| file2.rename(this, "layout2b.xml"); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.LAYOUT, "layout2b")); |
| assertFalse(resources.hasResourceItem(ResourceType.LAYOUT, "layout2")); |
| assertTrue(resources.getModificationCount() > generation); |
| } |
| |
| public void testRenameDrawableFile() throws Exception { |
| // rename drawable file |
| final VirtualFile file5 = myFixture.copyFileToProject(LAYOUT1, "res/drawable-xhdpi/foo2.png"); |
| ResourceFolderRepository resources = createRepository(); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.DRAWABLE, "foo2")); |
| assertFalse(resources.hasResourceItem(ResourceType.DRAWABLE, "foo3")); |
| ResourceItem item = getOnlyItem(resources, ResourceType.DRAWABLE, "foo2"); |
| assertTrue(item.getResourceValue(false) instanceof DensityBasedResourceValue); |
| DensityBasedResourceValue rv = (DensityBasedResourceValue)item.getResourceValue(false); |
| assertNotNull(rv); |
| assertSame(Density.XHIGH, rv.getResourceDensity()); |
| |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| file5.rename(this, "foo3.png"); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(resources.hasResourceItem(ResourceType.DRAWABLE, "foo3")); |
| assertFalse(resources.hasResourceItem(ResourceType.DRAWABLE, "foo2")); |
| assertTrue(resources.getModificationCount() > generation); |
| } |
| |
| public void testRenameValueFile() throws Exception { |
| final VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| |
| List<ResourceItem> items = resources.getResourceItem(ResourceType.STRING, "title_template_step"); |
| assertNotNull(items); |
| assertEquals(1, items.size()); |
| ResourceItem item = items.get(0); |
| assertEquals("myvalues.xml", item.getSource().getFile().getName()); |
| |
| // Renaming a value file should have no visible effect |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| file1.rename(this, "renamedvalues.xml"); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| items = resources.getResourceItem(ResourceType.STRING, "title_template_step"); |
| assertNotNull(items); |
| assertEquals(1, items.size()); |
| item = items.get(0); |
| assertEquals("renamedvalues.xml", item.getSource().getFile().getName()); |
| |
| // TODO: Optimize this such that there's no modification change for this. It's tricky because |
| // for file names we get separate notification from the old file deletion (beforePropertyChanged) |
| // and the new file name (propertyChanged). (Note that I tried performing the rename via a |
| // setName operation on the PsiFile instead of at the raw VirtualFile level, but the resulting |
| // events were the same.) |
| //assertEquals(generation, resources.getModificationCount()); |
| } |
| |
| public void testRenameValueFileToInvalid() throws Exception { |
| final VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| // After this rename, the values are no longer considered values since they're in an unrecognized file |
| file1.rename(this, "renamedvalues.badextension"); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| } |
| |
| private static ResourceItem getOnlyItem(ResourceFolderRepository repository, ResourceType type, String name) { |
| List<ResourceItem> item = repository.getResourceItem(type, name); |
| assertNotNull(item); |
| assertEquals(1, item.size()); |
| return item.get(0); |
| } |
| |
| public void testMoveFileResourceFileToNewConfiguration() throws Exception { |
| // Move a file-based resource file from one configuration to another; verify that |
| // items are preserved, generation changed (since it can affect config matching), |
| // and resource files updated. |
| final VirtualFile file1 = myFixture.copyFileToProject(LAYOUT1, "res/layout-land/layout1.xml"); |
| final VirtualFile file2 = myFixture.copyFileToProject(LAYOUT1, "res/layout-port/dummy.ignore"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| ResourceItem item = getOnlyItem(resources, ResourceType.LAYOUT, "layout1"); |
| assertEquals("land", item.getSource().getQualifiers()); |
| ResourceItem idItem = getOnlyItem(resources, ResourceType.ID, "btn_title_refresh"); |
| assertEquals("layout-land", idItem.getSource().getFile().getParentFile().getName()); |
| |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| // Move file from one location to another |
| file1.move(this, file2.getParent()); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.LAYOUT, "layout1")); |
| item = getOnlyItem(resources, ResourceType.LAYOUT, "layout1"); |
| assertEquals("port", item.getSource().getQualifiers()); |
| idItem = getOnlyItem(resources, ResourceType.ID, "btn_title_refresh"); |
| assertEquals("layout-port", idItem.getSource().getFile().getParentFile().getName()); |
| } |
| |
| public void testMoveValueResourceFileToNewConfiguration() throws Exception { |
| // Move a value file from one configuration to another; verify that |
| // items are preserved, generation changed (since it can affect config matching), |
| // and resource files updated. |
| final VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values-en/layout1.xml"); |
| final VirtualFile file2 = myFixture.copyFileToProject(VALUES1, "res/values-no/dummy.ignore"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| ResourceItem item = getOnlyItem(resources, ResourceType.STRING, "app_name"); |
| assertEquals("en", item.getSource().getQualifiers()); |
| assertEquals("en", item.getConfiguration().getLocaleQualifier().getLanguage()); |
| //noinspection ConstantConditions |
| assertEquals("Animations Demo", item.getResourceValue(false).getValue()); |
| |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| // Move file from one location to another |
| file1.move(this, file2.getParent()); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| item = getOnlyItem(resources, ResourceType.STRING, "app_name"); |
| assertEquals("no", item.getSource().getQualifiers()); |
| assertEquals("no", item.getConfiguration().getLocaleQualifier().getLanguage()); |
| //noinspection ConstantConditions |
| assertEquals("Animations Demo", item.getResourceValue(false).getValue()); |
| } |
| |
| public void testMoveResourceFileBetweenDensityFolders() throws Exception { |
| // Regression test for https://code.google.com/p/android/issues/detail?id=61648 |
| // Make sure we flush resource values when reusing resource items incrementally |
| |
| // Move a file-based resource file from one configuration to another; verify that |
| // items are preserved, generation changed (since it can affect config matching), |
| // and resource files updated. |
| final VirtualFile file1 = myFixture.copyFileToProject(DRAWABLE, "res/drawable-mdpi/picture.png"); |
| final VirtualFile file2 = myFixture.copyFileToProject(DRAWABLE, "res/drawable-hdpi/dummy.ignore"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| ResourceItem item = getOnlyItem(resources, ResourceType.DRAWABLE, "picture"); |
| ResourceFile source = item.getSource(); |
| assertNotNull(source); |
| assertEquals("mdpi", source.getQualifiers()); |
| ResourceValue resourceValue = item.getResourceValue(false); |
| assertNotNull(resourceValue); |
| String valuePath = resourceValue.getValue().replace(File.separatorChar, '/'); |
| assertTrue(valuePath, valuePath.endsWith("res/drawable-mdpi/picture.png")); |
| |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| // Move file from one location to another |
| file1.move(this, file2.getParent()); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.DRAWABLE, "picture")); |
| item = getOnlyItem(resources, ResourceType.DRAWABLE, "picture"); |
| source = item.getSource(); |
| assertNotNull(source); |
| assertEquals("hdpi", source.getQualifiers()); |
| resourceValue = item.getResourceValue(false); |
| assertNotNull(resourceValue); |
| valuePath = resourceValue.getValue().replace(File.separatorChar, '/'); |
| assertTrue(valuePath, valuePath.endsWith("res/drawable-hdpi/picture.png")); |
| } |
| |
| public void testMoveFileResourceFileToNewType() throws Exception { |
| // Move a file resource file file from one folder to another, changing the type |
| // (e.g. anim to animator), verify that resource types are updated |
| final VirtualFile file1 = myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| final VirtualFile file2 = myFixture.copyFileToProject(LAYOUT1, "res/menu/dummy.ignore"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.LAYOUT, "layout1")); |
| |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| file1.move(this, file2.getParent()); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.MENU, "layout1")); |
| assertFalse(resources.hasResourceItem(ResourceType.LAYOUT, "layout1")); |
| ResourceItem item = getOnlyItem(resources, ResourceType.MENU, "layout1"); |
| assertSame(ResourceFolderType.MENU, ((PsiResourceFile)item.getSource()).getFolderType()); |
| } |
| |
| public void testMoveOutOfResourceFolder() throws Exception { |
| // Move value files out of its resource folder; items should disappear |
| final VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| final VirtualFile javaFile = myFixture.copyFileToProject(VALUES1, "src/my/pkg/Dummy.java"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| file1.move(this, javaFile.getParent()); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| } |
| |
| public void testMoveIntoResourceFolder() throws Exception { |
| // Move value files out of its resource folder; items should disappear |
| final VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/dummy.ignore"); |
| final VirtualFile xmlFile = myFixture.copyFileToProject(VALUES1, "src/my/pkg/values.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| |
| final long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| xmlFile.move(this, file1.getParent()); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "title_template_step")); |
| } |
| |
| public void testReplaceResourceFile() throws Exception { |
| final VirtualFile file1 = myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.LAYOUT, "layout1")); |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "btn_title_refresh")); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "btn_title_refresh2")); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| myFixture.copyFileToProject(LAYOUT2, "res/layout/layout1.xml"); |
| } |
| catch (Exception e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| |
| // TODO: Find out how I can work around this! |
| // This doesn't work because copyFileToProject does not trigger PSI file notifications! |
| //assertTrue(resources.hasResourceItem(ResourceType.ID, "btn_title_refresh2")); |
| //assertFalse(resources.hasResourceItem(ResourceType.ID, "btn_title_refresh")); |
| //assertTrue(generation < resources.getModificationCount()); |
| } |
| |
| public void testAddEmptyValueFile() throws Exception { |
| myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| long generation = resources.getModificationCount(); |
| assertEquals(generation, resources.getModificationCount()); |
| } |
| |
| public void testRawFolder() throws Exception { |
| // In this folder, any file extension is allowed |
| myFixture.copyFileToProject(LAYOUT1, "res/raw/raw1.xml"); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> raw = resources.getItemsOfType(ResourceType.RAW); |
| assertEquals(1, raw.size()); |
| long generation = resources.getModificationCount(); |
| final VirtualFile file2 = myFixture.copyFileToProject(LAYOUT1, "res/raw/numbers.random"); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.RAW, "numbers")); |
| raw = resources.getItemsOfType(ResourceType.RAW); |
| assertEquals(2, raw.size()); |
| |
| generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| try { |
| file2.rename(this, "numbers2.whatever"); |
| } |
| catch (IOException e) { |
| fail(e.toString()); |
| } |
| } |
| }); |
| assertTrue(resources.getModificationCount() > generation); |
| assertTrue(resources.hasResourceItem(ResourceType.RAW, "numbers2")); |
| assertFalse(resources.hasResourceItem(ResourceType.RAW, "numbers")); |
| } |
| |
| public void testEditLayoutNoOp() throws Exception { |
| resetScanCounter(); |
| |
| // Make some miscellaneous edits in the file that have no bearing on the |
| // project resources and therefore end up doing no work |
| VirtualFile file1 = myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| final PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| assert(psiFile1 instanceof XmlFile); |
| final XmlFile xmlFile = (XmlFile)psiFile1; |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(1, layouts.size()); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout1")); |
| |
| final long initial = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| // Insert a comment at the beginning |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| XmlTag rootTag = xmlFile.getRootTag(); |
| assertNotNull(rootTag); |
| int rootTagOffset = rootTag.getTextOffset(); |
| document.insertString(rootTagOffset, "<!-- This is a\nmultiline comment -->"); |
| documentManager.commitDocument(document); |
| // Edit the comment some more |
| document.deleteString(rootTagOffset + 8, rootTagOffset + 8 + 5); |
| documentManager.commitDocument(document); |
| document.insertString(rootTagOffset + 8, "Replacement"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| // Inserting the comment and editing it shouldn't have had any observable results on the resource repository |
| assertEquals(initial, resources.getModificationCount()); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "noteArea")); |
| final XmlTag tag = findTagById(psiFile1, "noteArea"); |
| assertNotNull(tag); |
| |
| // Now insert some whitespace before a tag |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| int indentAreaBeforeTag = tag.getTextOffset() - 1; |
| document.insertString(indentAreaBeforeTag, " "); |
| documentManager.commitDocument(document); |
| document.deleteString(indentAreaBeforeTag, indentAreaBeforeTag + 2); |
| documentManager.commitDocument(document); |
| } |
| }); |
| // Space edits outside the tag shouldn't be observable |
| assertEquals(initial, resources.getModificationCount()); |
| |
| // Edit text inside an element tag. No effect in value files! |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final XmlTag header = findTagById(xmlFile, "header"); |
| assertNotNull(header); |
| int indentAreaBeforeTag = header.getSubTags()[0].getTextOffset(); |
| document.insertString(indentAreaBeforeTag, " "); |
| documentManager.commitDocument(document); |
| } |
| }); |
| // Space edits inside the tag shouldn't be observable |
| assertEquals(initial, resources.getModificationCount()); |
| |
| // Insert tag (without id) in layout file: ignored (only ids and file item matters) |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final XmlTag header = findTagById(xmlFile, "text2"); |
| assertNotNull(header); |
| int indentAreaBeforeTag = header.getTextOffset() - 1; |
| document.insertString(indentAreaBeforeTag, "<Button />"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| // Non-id new tags shouldn't be observable |
| assertEquals(initial, resources.getModificationCount()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| |
| // Finally make an edit which *does* affect the project resources to ensure |
| // that document edits actually *do* fire PSI events that are digested by |
| // this repository |
| final String elementDeclaration = "<Button android:id=\"@+id/newid\" />\n"; |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final XmlTag tag = findTagById(psiFile1, "noteArea"); |
| assertNotNull(tag); |
| document.insertString(tag.getTextOffset() - 1, elementDeclaration); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "newid")); |
| assertTrue(resources.getModificationCount() > initial); |
| |
| final long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| int startOffset = document.getText().indexOf(elementDeclaration); |
| document.deleteString(startOffset, startOffset + elementDeclaration.length()); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(resources.isScanPending(psiFile1)); |
| resetScanCounter(); |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "newid")); |
| assertTrue(resources.getModificationCount() > generation); |
| } |
| }); |
| UIUtil.dispatchAllInvocationEvents(); |
| } |
| }); |
| } |
| |
| public void testEditValueFileNoOp() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> strings = resources.getItemsOfType(ResourceType.STRING); |
| assertEquals(8, strings.size()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertTrue(resources.hasResourceItem(ResourceType.INTEGER, "card_flip_time_full")); |
| |
| long initial = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| // Edit comment header; should be a no-op |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| int offset = document.getText().indexOf("Licensed under the"); |
| document.insertString(offset, "This code is "); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertEquals(initial, resources.getModificationCount()); |
| |
| // Test edit text NOT under an item: no-op |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| int offset = document.getText().indexOf(" <item type=\"id\""); // insert BEFORE this |
| document.insertString(offset, "Ignored text"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertEquals(initial, resources.getModificationCount()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testInsertNewElementWithId() throws Exception { |
| resetScanCounter(); |
| |
| // Make some miscellaneous edits in the file that have no bearing on the |
| // project resources and therefore end up doing no work |
| VirtualFile file1 = myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| final PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| assert(psiFile1 instanceof XmlFile); |
| final XmlFile xmlFile = (XmlFile)psiFile1; |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(1, layouts.size()); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout1")); |
| |
| final long initial = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| // Insert tag (with an id) in layout file: should incrementally update set of ids |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final XmlTag header = findTagById(xmlFile, "text2"); |
| assertNotNull(header); |
| int indentAreaBeforeTag = header.getTextOffset() - 1; |
| document.insertString(indentAreaBeforeTag, |
| "<LinearLayout android:id=\"@+id/newid1\"><Child android:id=\"@+id/newid2\"/></LinearLayout>"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| // Currently, the PSI events delivered for the above edits results in PSI events without enough |
| // info for incremental analysis |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(initial < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "newid1")); |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "newid2")); |
| } |
| }); |
| } |
| |
| public void testEditIdAttributeValue() throws Exception { |
| resetScanCounter(); |
| // Edit the id attribute value of a layout item to change the set of available ids |
| VirtualFile file1 = myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(1, layouts.size()); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout1")); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "noteArea")); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "note2Area")); |
| |
| long generation = resources.getModificationCount(); |
| final XmlTag tag = findTagById(psiFile1, "noteArea"); |
| assertNotNull(tag); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| tag.setAttribute(ATTR_ID, ANDROID_URI, "@+id/note2Area"); |
| } |
| }); |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "note2Area")); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "noteArea")); |
| assertTrue(resources.getModificationCount() > generation); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testEditIdAttributeValue2() throws Exception { |
| // Edit the id attribute value: rather than by making a full value replacement, |
| // perform a tiny edit on the character content; this takes a different code |
| // path in the incremental updater |
| |
| resetScanCounter(); |
| // Edit the id attribute value of a layout item to change the set of available ids |
| VirtualFile file1 = myFixture.copyFileToProject(LAYOUT1, "res/layout/layout1.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> layouts = resources.getItemsOfType(ResourceType.LAYOUT); |
| assertEquals(1, layouts.size()); |
| assertNotNull(resources.getResourceItem(ResourceType.LAYOUT, "layout1")); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "noteArea")); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "note2Area")); |
| |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| // Edit value should cause update |
| long generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("noteArea"); |
| document.insertString(offset + 4, "2"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "note2Area")); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "noteArea")); |
| assertTrue(resources.getModificationCount() > generation); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testEditValueText() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> strings = resources.getItemsOfType(ResourceType.STRING); |
| assertEquals(8, strings.size()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertTrue(resources.hasResourceItem(ResourceType.INTEGER, "card_flip_time_full")); |
| |
| long generation = resources.getModificationCount(); |
| |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| // Edit value should cause update |
| final int screenSlideOffset = document.getText().indexOf("Screen Slide"); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.replaceString(screenSlideOffset + 3, screenSlideOffset + 3, "e"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| // NO revision bump yet, because the resource value hasn't been observed! |
| assertEquals(generation, resources.getModificationCount()); |
| |
| // Now observe it, do another edit, and see what happens |
| List<ResourceItem> labelList = resources.getResourceItem(ResourceType.STRING, "title_screen_slide"); |
| assertNotNull(labelList); |
| assertEquals(1, labelList.size()); |
| ResourceItem slideLabel = labelList.get(0); |
| ResourceValue resourceValue = slideLabel.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Screeen Slide", resourceValue.getValue()); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.deleteString(screenSlideOffset + 3, screenSlideOffset + 6); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| resourceValue = slideLabel.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Scrn Slide", resourceValue.getValue()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testNestedEditValueText() throws Exception { |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| List<ResourceItem> labelList = resources.getResourceItem(ResourceType.STRING, "title_template_step"); |
| assertNotNull(labelList); |
| assertEquals(1, labelList.size()); |
| ResourceItem label = labelList.get(0); |
| ResourceValue resourceValue = label.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Step ${step_number}: Lorem Ipsum", resourceValue.getValue()); |
| |
| long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| // Edit value should cause update |
| final int textOffset = document.getText().indexOf("Lorem"); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.insertString(textOffset + 1, "l"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| resourceValue = label.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Step ${step_number}: Llorem Ipsum", resourceValue.getValue()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testEditValueName() throws Exception { |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> strings = resources.getItemsOfType(ResourceType.STRING); |
| assertEquals(8, strings.size()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| |
| long generation = resources.getModificationCount(); |
| |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| final int offset = document.getText().indexOf("app_name"); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.replaceString(offset, offset + 3, "rap"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "rap_name")); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testAddValue() throws Exception { |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> strings = resources.getItemsOfType(ResourceType.STRING); |
| assertEquals(8, strings.size()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| |
| // Incrementally add in a new item |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| final int offset = document.getText().indexOf(" <item type"); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| String firstHalf = "<string name=\"new_s"; |
| String secondHalf = "tring\">New String</string>"; |
| document.insertString(offset, firstHalf); |
| documentManager.commitDocument(document); |
| document.insertString(offset + firstHalf.length(), secondHalf); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| // This currently doesn't work incrementally because we get psi events that do not contain |
| // enough info to be handled incrementally, so instead we do an asynchronous update (such that |
| // we can do a single update rather than rescanning the file 20 times) |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "new_string")); |
| //noinspection ConstantConditions |
| assertEquals("New String", resources.getResourceItem(ResourceType.STRING, "new_string").get(0).getResourceValue(false).getValue()); |
| } |
| }); |
| } |
| |
| public void testRemoveValue() throws Exception { |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> strings = resources.getItemsOfType(ResourceType.STRING); |
| assertEquals(8, strings.size()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| |
| long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| final String textToRemove = "<string name=\"app_name\">Animations Demo</string>"; |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| int offset = document.getText().indexOf(textToRemove); |
| document.deleteString(offset, offset + textToRemove.length()); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testChangeType() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.ID, "action_next")); |
| assertFalse(resources.hasResourceItem(ResourceType.DIMEN, "action_next")); |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| final int offset = document.getText().indexOf("\"id\" name=\"action_next\" />") + 1; |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.replaceString(offset, offset + 2, "dimen"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(generation < resources.getModificationCount()); |
| assertFalse(resources.hasResourceItem(ResourceType.ID, "action_next")); |
| assertTrue(resources.hasResourceItem(ResourceType.DIMEN, "action_next")); |
| } |
| }); |
| } |
| |
| public void testBreakNameAttribute() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| final int offset = document.getText().indexOf("name=\"app_name\">"); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.replaceString(offset + 2, offset + 3, "o"); // name => nome |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(generation < resources.getModificationCount()); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| } |
| }); |
| } |
| |
| public void testChangeValueTypeByTagNameEdit() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.INTEGER, "card_flip_time_half")); |
| |
| final long generation = resources.getModificationCount(); |
| final XmlTag tag = findTagByName(psiFile1, "card_flip_time_half"); |
| assertNotNull(tag); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| tag.setName("dimen"); // Change <integer> to <dimen> |
| } |
| }); |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.DIMEN, "card_flip_time_half")); |
| assertFalse(resources.hasResourceItem(ResourceType.INTEGER, "card_flip_time_half")); |
| } |
| }); |
| } |
| |
| public void testEditStyleName() throws Exception { |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "DarkTheme")); |
| |
| // Change style name |
| long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("DarkTheme"); |
| document.replaceString(offset, offset + 4, "Light"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertFalse(resources.hasResourceItem(ResourceType.STYLE, "DarkTheme")); |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "LightTheme")); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| |
| // Change style parent |
| generation = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("android:Theme.Holo"); |
| document.replaceString(offset, offset + "android:Theme.Holo".length(), "android:Theme.Light"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(resources.isScanPending(psiFile1)); |
| final long finalGeneration = generation; |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(finalGeneration < resources.getModificationCount()); |
| ResourceItem style = getOnlyItem(resources, ResourceType.STYLE, "LightTheme"); |
| ResourceValue resourceValue = style.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertTrue(resourceValue instanceof StyleResourceValue); |
| StyleResourceValue srv = (StyleResourceValue)resourceValue; |
| assertEquals("android:Theme.Light", srv.getParentStyle()); |
| ResourceValue actionBarStyle = srv.getItem("actionBarStyle", true); |
| assertNotNull(actionBarStyle); |
| assertEquals("@style/DarkActionBar", actionBarStyle.getValue()); |
| |
| // (We don't expect editing the style parent to be incremental) |
| } |
| }); |
| } |
| |
| public void testEditStyleItemText() throws Exception { |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "DarkTheme")); |
| ResourceItem style = getOnlyItem(resources, ResourceType.STYLE, "DarkTheme"); |
| StyleResourceValue srv = (StyleResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| ResourceValue actionBarStyle = srv.getItem("actionBarStyle", true); |
| assertNotNull(actionBarStyle); |
| assertEquals("@style/DarkActionBar", actionBarStyle.getValue()); |
| |
| long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("@style/DarkActionBar"); |
| document.replaceString(offset + 7, offset + 11, "Light"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "DarkTheme")); |
| |
| style = getOnlyItem(resources, ResourceType.STYLE, "DarkTheme"); |
| srv = (StyleResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| actionBarStyle = srv.getItem("actionBarStyle", true); |
| assertNotNull(actionBarStyle); |
| assertEquals("@style/LightActionBar", actionBarStyle.getValue()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testEditStyleItemName() throws Exception { |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "DarkTheme")); |
| ResourceItem style = getOnlyItem(resources, ResourceType.STYLE, "DarkTheme"); |
| StyleResourceValue srv = (StyleResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| ResourceValue actionBarStyle = srv.getItem("actionBarStyle", true); |
| assertNotNull(actionBarStyle); |
| assertEquals("@style/DarkActionBar", actionBarStyle.getValue()); |
| |
| long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("android:actionBarStyle"); |
| document.insertString(offset + 8, "in"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "DarkTheme")); |
| |
| style = getOnlyItem(resources, ResourceType.STYLE, "DarkTheme"); |
| srv = (StyleResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| actionBarStyle = srv.getItem("inactionBarStyle", true); |
| assertNotNull(actionBarStyle); |
| assertEquals("@style/DarkActionBar", actionBarStyle.getValue()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testEditDeclareStyleableAttr() throws Exception { |
| // Check edits of the name in a <declare-styleable> element. |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "watchType")); |
| ResourceItem style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomView"); |
| DeclareStyleableResourceValue srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| AttrResourceValue watchType = findAttr(srv.getAllAttributes(), "watchType"); |
| assertNotNull(watchType); |
| assertEquals(2, watchType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(1), watchType.getAttributeValues().get("type_stopwatch")); |
| assertEquals(Integer.valueOf(0), watchType.getAttributeValues().get("type_countdown")); |
| AttrResourceValue crash = findAttr(srv.getAllAttributes(), "crash"); |
| assertNotNull(crash); |
| assertNull(crash.getAttributeValues()); |
| |
| AttrResourceValue minWidth = findAttr(srv.getAllAttributes(), "minWidth"); |
| assertNotNull(minWidth); |
| assertFalse(resources.hasResourceItem(ResourceType.ATTR, "minWidth")); |
| |
| long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("MyCustomView"); |
| document.insertString(offset + 8, "er"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomerView")); |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "watchType")); |
| assertFalse(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomerView"); |
| srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| watchType = findAttr(srv.getAllAttributes(), "watchType"); |
| assertNotNull(watchType); |
| assertEquals(2, watchType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(1), watchType.getAttributeValues().get("type_stopwatch")); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testEditAttr() throws Exception { |
| // Insert, remove and change <attr> attributes inside a <declare-styleable> and ensure that |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| // Fetch resource value to ensure it gets replaced after update |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "watchType")); |
| ResourceItem style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomView"); |
| DeclareStyleableResourceValue srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| AttrResourceValue watchType = findAttr(srv.getAllAttributes(), "watchType"); |
| assertNotNull(watchType); |
| assertEquals(2, watchType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(1), watchType.getAttributeValues().get("type_stopwatch")); |
| assertEquals(Integer.valueOf(0), watchType.getAttributeValues().get("type_countdown")); |
| AttrResourceValue crash = findAttr(srv.getAllAttributes(), "crash"); |
| assertNotNull(crash); |
| assertNull(crash.getAttributeValues()); |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("watchType"); |
| document.insertString(offset, "w"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertFalse(resources.isScanPending(psiFile1)); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| assertFalse(resources.hasResourceItem(ResourceType.ATTR, "watchType")); |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "wwatchType")); |
| style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomView"); |
| srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| watchType = findAttr(srv.getAllAttributes(), "wwatchType"); |
| assertNotNull(watchType); |
| assertEquals(2, watchType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(1), watchType.getAttributeValues().get("type_stopwatch")); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| |
| // Now insert a new item and delete one and make sure we're still okay |
| resetScanCounter(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| String crashAttr = "<attr name=\"crash\" format=\"boolean\" />"; |
| final int offset = document.getText().indexOf(crashAttr); |
| document.deleteString(offset, offset + crashAttr.length()); |
| document.insertString(offset, "<attr name=\"newcrash\" format=\"integer\" />"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| assertFalse(resources.hasResourceItem(ResourceType.ATTR, "watchType")); |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "wwatchType")); |
| ResourceItem style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomView"); |
| DeclareStyleableResourceValue srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| AttrResourceValue watchType = findAttr(srv.getAllAttributes(), "wwatchType"); |
| assertNotNull(watchType); |
| assertEquals(2, watchType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(1), watchType.getAttributeValues().get("type_stopwatch")); |
| assertEquals(Integer.valueOf(0), watchType.getAttributeValues().get("type_countdown")); |
| AttrResourceValue crash = findAttr(srv.getAllAttributes(), "crash"); |
| assertNull(crash); |
| AttrResourceValue newcrash = findAttr(srv.getAllAttributes(), "newcrash"); |
| assertNotNull(newcrash); |
| assertNull(newcrash.getAttributeValues()); |
| } |
| }); |
| } |
| |
| public void testEditDeclareStyleableFlag() throws Exception { |
| // Rename, add and remove <flag> and <enum> nodes under a declare styleable and assert |
| // that the declare styleable parent is updated |
| resetScanCounter(); |
| |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| final PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| // Fetch resource value to ensure it gets replaced after update |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "watchType")); |
| assertFalse(resources.hasResourceItem(ResourceType.ATTR, "ignore_no_format")); |
| final ResourceItem style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomView"); |
| final DeclareStyleableResourceValue srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| final AttrResourceValue flagType = findAttr(srv.getAllAttributes(), "flagType"); |
| assertNotNull(flagType); |
| assertEquals(2, flagType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(16), flagType.getAttributeValues().get("flag1")); |
| assertEquals(Integer.valueOf(32), flagType.getAttributeValues().get("flag2")); |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("flag1"); |
| document.insertString(offset + 1, "l"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "flagType")); |
| ResourceItem style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomView"); |
| DeclareStyleableResourceValue srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| AttrResourceValue flagType = findAttr(srv.getAllAttributes(), "flagType"); |
| assertNotNull(flagType); |
| assertEquals(2, flagType.getAttributeValues().size()); |
| assertNull(flagType.getAttributeValues().get("flag1")); |
| assertEquals(Integer.valueOf(16), flagType.getAttributeValues().get("fllag1")); |
| |
| // Now insert a new enum and delete one and make sure we're still okay |
| resetScanCounter(); |
| long nextGeneration = resources.getModificationCount(); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| String enumAttr = "<enum name=\"type_stopwatch\" value=\"1\"/>"; |
| int offset = document.getText().indexOf(enumAttr); |
| document.deleteString(offset, offset + enumAttr.length()); |
| String flagAttr = "<flag name=\"flag2\" value=\"0x20\"/>"; |
| offset = document.getText().indexOf(flagAttr); |
| document.insertString(offset, "<flag name=\"flag3\" value=\"0x40\"/>"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| assertTrue(nextGeneration < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.DECLARE_STYLEABLE, "MyCustomView")); |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "watchType")); |
| assertTrue(resources.hasResourceItem(ResourceType.ATTR, "flagType")); |
| style = getOnlyItem(resources, ResourceType.DECLARE_STYLEABLE, "MyCustomView"); |
| srv = (DeclareStyleableResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| assertEquals(5, srv.getAllAttributes().size()); |
| flagType = findAttr(srv.getAllAttributes(), "flagType"); |
| assertNotNull(flagType); |
| assertEquals(3, flagType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(16), flagType.getAttributeValues().get("fllag1")); |
| assertEquals(Integer.valueOf(32), flagType.getAttributeValues().get("flag2")); |
| assertEquals(Integer.valueOf(64), flagType.getAttributeValues().get("flag3")); |
| |
| AttrResourceValue watchType = findAttr(srv.getAllAttributes(), "watchType"); |
| assertNotNull(watchType); |
| assertEquals(1, watchType.getAttributeValues().size()); |
| assertEquals(Integer.valueOf(0), watchType.getAttributeValues().get("type_countdown")); |
| } |
| }); |
| } |
| |
| public void testEditPluralItems() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| // Test that our tools:quantity works correctly for getResourceValue() |
| assertTrue(resources.hasResourceItem(ResourceType.PLURALS, "my_plural")); |
| ResourceItem plural = getOnlyItem(resources, ResourceType.PLURALS, "my_plural"); |
| ResourceValue resourceValue = plural.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("@string/hello_two", resourceValue.getValue()); |
| |
| // TODO: It would be nice to avoid updating the generation if you |
| // edit a different item than the one being picked (default or via |
| // tools:quantity) but for now we're not worrying about that optimization |
| |
| long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("@string/hello_two"); |
| document.replaceString(offset + 9, offset + 10, "a"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| plural = getOnlyItem(resources, ResourceType.PLURALS, "my_plural"); |
| resourceValue = plural.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("@string/hallo_two", resourceValue.getValue()); |
| assertTrue(generation < resources.getModificationCount()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testEditArrayItemText() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| // Test that our tools:index and fallback handling for arrays works correctly |
| // for getResourceValue() |
| assertTrue(resources.hasResourceItem(ResourceType.ARRAY, "security_questions")); |
| ResourceItem array = getOnlyItem(resources, ResourceType.ARRAY, "security_questions"); |
| ResourceValue resourceValue = array.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Question 4", resourceValue.getValue()); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.ARRAY, "integers")); |
| array = getOnlyItem(resources, ResourceType.ARRAY, "integers"); |
| resourceValue = array.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("10", resourceValue.getValue()); |
| |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("Question 4"); |
| document.insertString(offset, "Q"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.ARRAY, "security_questions")); |
| array = getOnlyItem(resources, ResourceType.ARRAY, "security_questions"); |
| resourceValue = array.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("QQuestion 4", resourceValue.getValue()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testAddArrayItemElements() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| final int offset = document.getText().indexOf("<item>Question 3</item>"); |
| document.insertString(offset, "<item>Question 2.5</item>"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.ARRAY, "security_questions")); |
| ResourceItem array = getOnlyItem(resources, ResourceType.ARRAY, "security_questions"); |
| ResourceValue resourceValue = array.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Question 3", resourceValue.getValue()); |
| assertTrue(resourceValue instanceof ArrayResourceValue); |
| ArrayResourceValue arv = (ArrayResourceValue)resourceValue; |
| assertEquals(6, arv.getElementCount()); |
| assertEquals("Question 2", arv.getElement(1)); |
| assertEquals("Question 2.5", arv.getElement(2)); |
| assertEquals("Question 3", arv.getElement(3)); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testRemoveArrayItemElements() throws Exception { |
| resetScanCounter(); |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES1, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| String elementString = "<item>Question 3</item>"; |
| final int offset = document.getText().indexOf(elementString); |
| document.deleteString(offset, offset + elementString.length()); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.ARRAY, "security_questions")); |
| ResourceItem array = getOnlyItem(resources, ResourceType.ARRAY, "security_questions"); |
| ResourceValue resourceValue = array.getResourceValue(false); |
| assertNotNull(resourceValue); |
| assertEquals("Question 5", resourceValue.getValue()); |
| assertTrue(resourceValue instanceof ArrayResourceValue); |
| ArrayResourceValue arv = (ArrayResourceValue)resourceValue; |
| assertEquals(4, arv.getElementCount()); |
| |
| // Shouldn't have done any full file rescans during the above edits |
| ensureIncremental(); |
| } |
| |
| public void testGradualEdits() throws Exception { |
| resetScanCounter(); |
| |
| // Gradually type in the contents of a value file and make sure we end up with a valid view of the world |
| VirtualFile file1 = myFixture.copyFileToProject(VALUES_EMPTY, "res/values/myvalues.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.deleteString(0, document.getTextLength()); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| final String contents = |
| "<!--\n" + |
| " -->\n" + |
| "\n" + |
| "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" + |
| "\n" + |
| " <!-- Titles -->\n" + |
| " <string name=\"app_name\">Animations Demo</string>\n" + |
| " <string name=\"title_zoom\">Zoom</string>\n" + |
| " <string name=\"title_layout_changes\">Layout Changes</string>\n" + |
| " <string name=\"title_template_step\">Step <xliff:g id=\"step_number\">%1$d</xliff:g>: Lorem\n" + |
| " Ipsum</string>\n" + |
| " <string name=\"ellipsis\">Here it is: \\u2026!</string>\n" + |
| "\n" + |
| " <item type=\"id\" name=\"action_next\" />\n" + |
| "\n" + |
| " <style name=\"DarkActionBar\" parent=\"android:Widget.Holo.ActionBar\">\n" + |
| " <item name=\"android:background\">@android:color/transparent</item>\n" + |
| " </style>\n" + |
| "\n" + |
| " <integer name=\"card_flip_time_half\">150</integer>\n" + |
| "\n" + |
| " <declare-styleable name=\"MyCustomView\">\n" + |
| " <attr name=\"watchType\" format=\"enum\">\n" + |
| " <enum name=\"type_countdown\" value=\"0\"/>\n" + |
| " </attr>\n" + |
| " <attr name=\"crash\" format=\"boolean\" />\n" + |
| " </declare-styleable>\n" + |
| "</resources>\n"; |
| for (int i = 0; i < contents.length(); i++) { |
| final int offset = i; |
| final char character = contents.charAt(i); |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| document.insertString(offset, String.valueOf(character)); |
| documentManager.commitDocument(document); |
| } |
| }); |
| } |
| |
| assertTrue(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "DarkActionBar")); |
| |
| assertTrue(resources.hasResourceItem(ResourceType.STYLE, "DarkActionBar")); |
| ResourceItem style = getOnlyItem(resources, ResourceType.STYLE, "DarkActionBar"); |
| StyleResourceValue srv = (StyleResourceValue)style.getResourceValue(false); |
| assertNotNull(srv); |
| ResourceValue actionBarStyle = srv.getItem("background", true); |
| assertNotNull(actionBarStyle); |
| assertEquals("@android:color/transparent", actionBarStyle.getValue()); |
| //noinspection ConstantConditions |
| assertEquals("Zoom", getOnlyItem(resources, ResourceType.STRING, "title_zoom").getResourceValue(false).getValue()); |
| } |
| }); |
| } |
| |
| public void testIssue56799() throws Exception { |
| // Test deleting a string; ensure that the whole repository is updated correctly. |
| // Regression test for |
| // https://code.google.com/p/android/issues/detail?id=56799 |
| VirtualFile file1 = myFixture.copyFileToProject(STRINGS, "res/values/strings.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "app_name2")); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "hello_world")); |
| |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| String string = " <string name=\"hello_world\">Hello world!</string>"; |
| final int offset = document.getText().indexOf(string); |
| assertTrue(offset != -1); |
| document.deleteString(offset, offset + string.length()); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(generation < resources.getModificationCount()); |
| |
| assertTrue(resources.isScanPending(psiFile1)); |
| resetScanCounter(); |
| UIUtil.dispatchAllInvocationEvents(); |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| ensureSingleScan(); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "hello_world")); |
| } |
| }); |
| } |
| |
| public void testEditXmlProcessingInstructionAttr() throws Exception { |
| // Test editing an attribute in the XML prologue |
| VirtualFile file1 = myFixture.copyFileToProject(STRINGS, "res/values/strings.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "app_name2")); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "hello_world")); |
| |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| String string = "utf-8"; |
| final int offset = document.getText().indexOf(string); |
| assertTrue(offset != -1); |
| document.insertString(offset, "t"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| // Edits in XML processing instructions have no effect on the resource repository |
| assertEquals(generation, resources.getModificationCount()); |
| assertFalse(resources.isScanPending(psiFile1)); |
| UIUtil.dispatchAllInvocationEvents(); |
| } |
| |
| public void testIssue64239() throws Exception { |
| // If you duplicate a string, then change its contents (which still duplicated), |
| // and then finally rename the string, then the value of the second clone will |
| // continue to be referred from the first string: |
| // <string name="foo">value 1</foo> |
| // <string name="foo">value 2</foo> |
| // then change the second string name to |
| // <string name="foo2">value 2</foo> |
| // If you now evaluate the value of foo, you get "value 1". Basically while the |
| // two strings are (illegally) aliasing, the value of the first string is replaced. |
| |
| // TODO: Test both *duplicating* a node, as well as manually typing in a brand |
| // new one with the same result |
| |
| VirtualFile file1 = myFixture.copyFileToProject(STRINGS, "res/values/strings.xml"); |
| PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| //noinspection ConstantConditions |
| assertEquals("My Application 574", |
| resources.getResourceItem(ResourceType.STRING, "app_name").get(0).getResourceValue(false).getValue()); |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| final int offset = document.getText().indexOf("</resources>"); |
| assertTrue(offset != -1); |
| final String string = "<string name=\"app_name\">New Value</string>"; |
| |
| // First duplicate the line: |
| WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() { |
| @Override |
| public void run() { |
| document.insertString(offset, string); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertTrue(generation < resources.getModificationCount()); |
| resetScanCounter(); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| |
| // Then replace the name of the duplicated string |
| WriteCommandAction.runWriteCommandAction(getProject(), new Runnable() { |
| @Override |
| public void run() { |
| int startOffset = offset + "<string name=\"".length(); |
| document.replaceString(startOffset, startOffset + "app_name".length(), "new_name"); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| assertFalse(resources.isScanPending(psiFile1)); |
| assertTrue(generation < resources.getModificationCount()); |
| resetScanCounter(); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "new_name")); |
| |
| //noinspection ConstantConditions |
| assertEquals("New Value", |
| resources.getResourceItem(ResourceType.STRING, "new_name").get(0).getResourceValue(false).getValue()); |
| //noinspection ConstantConditions |
| assertEquals("My Application 574", |
| resources.getResourceItem(ResourceType.STRING, "app_name").get(0).getResourceValue(false).getValue()); |
| } |
| |
| public void testIdScanFromLayout() throws Exception { |
| // Test for http://b.android.com/172239 |
| myFixture.copyFileToProject(LAYOUT_ID_SCAN, "res/layout/layout1.xml"); |
| ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| Collection<String> ids = resources.getItemsOfType(ResourceType.ID); |
| assertNotNull(ids); |
| assertContainsElements(ids, "header", "image", "styledView", "imageView", "imageView2", "imageButton", "nonExistent"); |
| } |
| |
| public void testSync() throws Exception { |
| // Regression test for https://code.google.com/p/android/issues/detail?id=79629 |
| // Ensure that sync() handles rescanning immediately |
| VirtualFile file1 = myFixture.copyFileToProject(STRINGS, "res/values/strings.xml"); |
| final PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "app_name2")); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "hello_world")); |
| |
| final long generation = resources.getModificationCount(); |
| final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); |
| final Document document = documentManager.getDocument(psiFile1); |
| assertNotNull(document); |
| |
| UIUtil.invokeAndWaitIfNeeded(new Runnable() { |
| @Override |
| public void run() { |
| // The sync() call must be called from the dispatch thread |
| WriteCommandAction.runWriteCommandAction(null, new Runnable() { |
| @Override |
| public void run() { |
| String string = " <string name=\"hello_world\">Hello world!</string>"; |
| final int offset = document.getText().indexOf(string); |
| assertTrue(offset != -1); |
| |
| // Simulate an edit event that triggers the incremental updater to |
| // give up and schedule a subsequent update instead. (This used to be |
| // the case here, but as of IntelliJ 14.1 it's now delivering more |
| // accurate events for the below edits, which made the sync-test |
| // fail because it would already have correct results *before* the |
| // sync. Therefore, we simply trigger a pending scan (which stops |
| // subequent incremental events from being processed). |
| resources.rescan(psiFile1, ResourceFolderType.VALUES); |
| |
| document.deleteString(offset, offset + string.length()); |
| documentManager.commitDocument(document); |
| } |
| }); |
| |
| // The strings file contains definitions for app_name, action_settings and hello_world. |
| // We've manually deleted the hello_world string. We now check that app_name remains |
| // in the resource set, and that hello_world is removed. (We check that hello_world |
| // is there before the sync, and gone after.) |
| assertTrue(resources.isScanPending(psiFile1)); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "hello_world")); |
| |
| assertTrue(ApplicationManager.getApplication().isDispatchThread()); |
| resources.sync(); |
| assertTrue(generation < resources.getModificationCount()); |
| assertTrue(resources.hasResourceItem(ResourceType.STRING, "app_name")); |
| assertFalse(resources.hasResourceItem(ResourceType.STRING, "hello_world")); |
| assertFalse(resources.isScanPending(psiFile1)); |
| } |
| }); |
| } |
| |
| public void testDataBindingVariables() { |
| VirtualFile file1 = myFixture.copyFileToProject(LAYOUT_WITH_DATA_BINDING, "res/layout/layout_with_data_binding.xml"); |
| final PsiFile psiFile1 = PsiManager.getInstance(getProject()).findFile(file1); |
| assertNotNull(psiFile1); |
| final ResourceFolderRepository resources = createRepository(); |
| assertNotNull(resources); |
| AndroidFacet facet = resources.getFacet(); |
| assertEquals(1, resources.getDataBindingResourceFiles().size()); |
| final String appPackage = DataBindingUtil.getGeneratedPackageName(facet); |
| final DataBindingInfo info = resources.getDataBindingResourceFiles().get(appPackage + ".databinding.LayoutWithDataBindingBinding"); |
| assertNotNull(info); |
| List<PsiDataBindingResourceItem> variables = info.getItems(DataBindingResourceType.VARIABLE); |
| assertEquals(1, variables.size()); |
| final PsiDataBindingResourceItem variable1 = variables.get(0); |
| assertEquals("variable1", variable1.getName()); |
| assertEquals("String", variable1.getExtra(SdkConstants.ATTR_TYPE)); |
| assertNotNull(variable1.getXmlTag()); |
| |
| List<PsiDataBindingResourceItem> imports = new ArrayList<PsiDataBindingResourceItem>();// clone to be able to sort |
| imports.addAll(info.getItems(DataBindingResourceType.IMPORT)); |
| assertEquals(2, imports.size()); |
| |
| Collections.sort(imports, new Comparator<PsiDataBindingResourceItem>() { |
| @Override |
| public int compare(PsiDataBindingResourceItem item1, PsiDataBindingResourceItem item2) { |
| return item1.getExtra(SdkConstants.ATTR_TYPE).compareTo(item2.getExtra(SdkConstants.ATTR_TYPE)); |
| } |
| }); |
| |
| PsiDataBindingResourceItem import1 = imports.get(0); |
| assertEquals("p1.p2.import1", import1.getExtra(SdkConstants.ATTR_TYPE)); |
| assertNull(import1.getExtra(SdkConstants.ATTR_ALIAS)); |
| assertNotNull(import1.getXmlTag()); |
| |
| PsiDataBindingResourceItem import2 = imports.get(1); |
| assertEquals("p1.p2.import2", import2.getExtra(SdkConstants.ATTR_TYPE)); |
| assertEquals("i2", import2.getExtra(SdkConstants.ATTR_ALIAS)); |
| assertNotNull(import2.getXmlTag()); |
| |
| List<DataBindingInfo.ViewWithId> viewsWithIds = info.getViewsWithIds(); |
| assertEquals(6, viewsWithIds.size()); |
| Collections.sort(viewsWithIds, new Comparator<DataBindingInfo.ViewWithId>() { |
| @Override |
| public int compare(DataBindingInfo.ViewWithId v1, DataBindingInfo.ViewWithId v2) { |
| return v1.name.compareTo(v2.name); |
| } |
| }); |
| validateViewWithId(facet, viewsWithIds.get(0), "foo.bar.Magic", "magicView"); |
| validateViewWithId(facet, viewsWithIds.get(1), "android.view.View", "normalViewTag"); |
| validateViewWithId(facet, viewsWithIds.get(2), "android.view.SurfaceView", "surfaceView1"); |
| validateViewWithId(facet, viewsWithIds.get(3), "android.widget.TextView", "textView1"); |
| validateViewWithId(facet, viewsWithIds.get(4), "android.view.ViewGroup", "viewTag"); |
| validateViewWithId(facet, viewsWithIds.get(5), "android.webkit.WebView", "webView1"); |
| } |
| |
| private void validateViewWithId(AndroidFacet facet, DataBindingInfo.ViewWithId viewWithId, String qualified, String variableName) { |
| assertTrue(DataBindingUtil.resolveViewPsiType(viewWithId, facet).equalsToText(qualified)); |
| assertEquals(variableName, viewWithId.name); |
| } |
| |
| @Nullable |
| private static XmlTag findTagById(@NotNull PsiFile file, @NotNull String id) { |
| assertFalse(id.startsWith(PREFIX_RESOURCE_REF)); // just the id |
| String newId = NEW_ID_PREFIX + id; |
| String oldId = ID_PREFIX + id; |
| for (XmlTag tag : PsiTreeUtil.findChildrenOfType(file, XmlTag.class)) { |
| String tagId = tag.getAttributeValue(ATTR_ID, ANDROID_URI); |
| if (newId.equals(tagId) || oldId.equals(tagId)) { |
| return tag; |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| private static XmlTag findTagByName(@NotNull PsiFile file, @NotNull String name) { |
| for (XmlTag tag : PsiTreeUtil.findChildrenOfType(file, XmlTag.class)) { |
| String tagName = tag.getAttributeValue(ATTR_NAME); |
| if (name.equals(tagName)) { |
| return tag; |
| } |
| } |
| return null; |
| } |
| |
| @Nullable |
| private static AttrResourceValue findAttr(@NotNull List<AttrResourceValue> attrs, @NotNull String name) { |
| for (AttrResourceValue attr : attrs) { |
| if (attr.getName().equals(name)) { |
| return attr; |
| } |
| } |
| return null; |
| } |
| } |