blob: f72166c2e7580c5105847bfc9f089a55cf56b573 [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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 org.jetbrains.android;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.util.Segment;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.testFramework.fixtures.JavaCodeInsightTestFixture;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.PsiElementUsageTarget;
import com.intellij.usages.UsageTarget;
import com.intellij.usages.UsageTargetUtil;
import org.jetbrains.annotations.NonNls;
import java.util.*;
/**
* @author Eugene.Kudelevsky
*/
public class AndroidFindUsagesTest extends AndroidTestCase {
private static final String BASE_PATH = "/findUsages/";
@Override
public void setUp() throws Exception {
super.setUp();
myFixture.copyFileToProject(BASE_PATH + "picture3.gif", "res/drawable/picture3.gif");
setupR();
}
/**
* This method is overridden to test alternate methods of supplying the top level R class.
*/
protected void setupR() {
myFixture.copyFileToProject("R.java", "gen/p1/p2/R.java");
}
public List<UsageInfo> findCodeUsages(String path, String pathInProject) throws Throwable {
Collection<UsageInfo> usages = findUsages(path, myFixture, pathInProject);
List<UsageInfo> result = new ArrayList<UsageInfo>();
for (UsageInfo usage : usages) {
if (!usage.isNonCodeUsage) {
result.add(usage);
}
}
return result;
}
public void testFileResource() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "styles.xml", "res/values/styles.xml");
List<UsageInfo> references = findCodeUsages("fu1_layout.xml", "res/layout/fu1_layout.xml");
assertEquals(3, references.size());
assertEquals("Class.java:5:\n" +
" int id1 = R.drawable.picture3;\n" +
" |~~~~~~~~\n" +
"layout/fu1_layout.xml:3:\n" +
" <TextView android:background=\"@drawable/picture3\"/>\n" +
" |~~~~~~~~~~~~~~~~~~ \n" +
"values/styles.xml:3:\n" +
" <item name=\"android:windowBackground\">@drawable/picture3</item>\n" +
" |~~~~~~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
Collection<UsageInfo> references = findCodeUsages("fu2_layout.xml", "res/layout/fu2_layout.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/fu2_layout.xml:3:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource1() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu1_values.xml", "res/values/fu1_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource2() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu2_values.xml", "res/values/fu2_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource3() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu3_values.xml", "res/values/fu3_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource4() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu4_values.xml", "res/values/fu4_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource5() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu5_values.xml", "res/values/fu5_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource6() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu6_values.xml", "res/values/fu6_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource7() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu7_values.xml", "res/values/fu7_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResource8() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("fu8_values.xml", "res/values/f8_values.xml");
assertEquals(2, references.size());
assertEquals("Class.java:6:\n" +
" int id2 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testStyleInheritance() throws Throwable {
Collection<UsageInfo> references = findCodeUsages("fu10_values.xml", "res/values/f10_values.xml");
assertEquals(3, references.size());
assertEquals("values/f10_values.xml:6:\n" +
" <style name=\"myStyle.s\">\n" +
" |~~~~~~~ \n" +
"values/f10_values.xml:10:\n" +
" <style name=\"style1\" parent=\"myStyle\">\n" +
" |~~~~~~~ \n" +
"values/f10_values.xml:14:\n" +
" <style name=\"myStyle.s.a\">\n" +
" |~~~~~~~ \n",
describeUsages(references));
}
public void testStyleInheritance1() throws Throwable {
Collection<UsageInfo> references = findCodeUsages("fu11_values.xml", "res/values/f11_values.xml");
assertEquals(3, references.size());
assertEquals("values/f11_values.xml:6:\n" +
" <style name=\"myStyle.s\">\n" +
" |~~~~~~~ \n" +
"values/f11_values.xml:10:\n" +
" <style name=\"style1\" parent=\"myStyle\">\n" +
" |~~~~~~~ \n" +
"values/f11_values.xml:14:\n" +
" <style name=\"myStyle.s.a\">\n" +
" |~~~~~~~ \n",
describeUsages(references));
}
public void testValueItemResource() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
Collection<UsageInfo> references = findCodeUsages("fu5_layout.xml", "res/layout/fu5_layout.xml");
assertEquals(2, references.size());
assertEquals("Class.java:7:\n" +
" int id3 = R.string.hi;\n" +
" |~~\n" +
"layout/fu5_layout.xml:3:\n" +
" <TextView android:text=\"@string/hi\"/>\n" +
" |~~~~~~~~~~ \n",
describeUsages(references));
}
public void testFileResourceField() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("Fu3.java", "src/p1/p2/Fu3.java");
assertEquals(2, references.size());
assertEquals("Fu3.java:5:\n" +
" int id1 = R.drawable.picture3;\n" +
" |~~~~~~~~\n" +
"layout/layout.xml:3:\n" +
" android:background=\"@drawable/picture3\">\n" +
" |~~~~~~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueResourceField() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("Fu4.java", "src/p1/p2/Fu4.java");
assertEquals(2, references.size());
assertEquals("Fu4.java:5:\n" +
" int id1 = R.string.hello;\n" +
" |~~~~~\n" +
"layout/layout.xml:5:\n" +
" <TextView android:text=\"@string/hello\"/>\n" +
" |~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testValueItemResourceField() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("Fu6.java", "src/p1/p2/Fu6.java");
assertEquals(2, references.size());
assertEquals("Fu6.java:5:\n" +
" int id1 = R.string.hi;\n" +
" |~~\n" +
"layout/layout.xml:6:\n" +
" <TextView android:text=\"@string/hi\"/>\n" +
" |~~~~~~~~~~ \n",
describeUsages(references));
}
public void testIdResource() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
Collection<UsageInfo> references = findCodeUsages("fu7_layout.xml", "res/layout/fu7_layout.xml");
assertEquals(2, references.size());
assertEquals("Class.java:8:\n" +
" int id3 = R.id.anchor;\n" +
" |~~~~~~\n" +
"layout/fu7_layout.xml:5:\n" +
" <TextView android:layout_alignRight=\"@id/anchor\"/>\n" +
" |~~~~~~~~~~ \n",
describeUsages(references));
}
public void testIdResourceField() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "layout.xml", "res/layout/layout.xml");
Collection<UsageInfo> references = findCodeUsages("Fu8.java", "src/p1/p2/Fu8.java");
assertEquals(2, references.size());
assertEquals("Fu8.java:5:\n" +
" int id1 = R.id.anchor;\n" +
" |~~~~~~\n" +
"layout/layout.xml:7:\n" +
" <TextView android:layout_alignRight=\"@id/anchor\"/>\n" +
" |~~~~~~~~~~ \n",
describeUsages(references));
}
public void testIdResourceDeclaration() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
Collection<UsageInfo> references = findCodeUsages("fu9_layout.xml", "res/layout/fu9_layout.xml");
assertEquals(2, references.size());
assertEquals("Class.java:8:\n" +
" int id3 = R.id.anchor;\n" +
" |~~~~~~\n" +
"layout/fu9_layout.xml:5:\n" +
" <TextView android:layout_alignRight=\"@id/anchor\"/>\n" +
" |~~~~~~~~~~ \n",
describeUsages(references));
}
public void testStringArray() throws Throwable {
myFixture.copyFileToProject(BASE_PATH + "strings.xml", "res/values/strings.xml");
myFixture.copyFileToProject(BASE_PATH + "Class.java", "src/p1/p2/Class.java");
Collection<UsageInfo> references = findCodeUsages("stringArray.xml", "res/layout/stringArray.xml");
assertEquals(2, references.size());
assertEquals("Class.java:9:\n" +
" int id4 = R.array.str_arr;\n" +
" |~~~~~~~\n" +
"layout/stringArray.xml:3:\n" +
" <ListView android:entries=\"@array/str_arr\"/>\n" +
" |~~~~~~~~~~~~~~ \n",
describeUsages(references));
}
public void testStyleable() throws Throwable {
createManifest();
myFixture.copyFileToProject(BASE_PATH + "attrs.xml", "res/values/attrs.xml");
myFixture.copyFileToProject(BASE_PATH + "R_MyView.java", "src/p1/p2/R.java");
Collection<UsageInfo> references = findCodeUsages("MyView1.java", "src/p1/p2/MyView.java");
//noinspection SpellCheckingInspection
assertEquals("MyView.java:13:\n" +
" TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView);\n" +
" |~~~~~~ \n" +
"MyView.java:14:\n" +
" int answer = a.getInt(R.styleable.MyView_answer, 0);\n" +
" |~~~~~~~~~~~~~ \n" +
"R.java:46:\n" +
" <tr><td><code>{@link #MyView_answer p1.p2:answer}</code></td><td></td></tr>\n" +
" |~~~~~~~~~~~~~ \n" +
"R.java:48:\n" +
" @see #MyView_answer\n" +
" |~~~~~~~~~~~~\n" +
"R.java:55:\n" +
" attribute's value can be found in the {@link #MyView} array.\n" +
" |~~~~~~ \n",
// Note: the attrs.xml occurence of "MyView" is not a *reference* to the *field*,
// the field is a reference to the XML:
// I had earlier implemented this such the following showed up (by
// making the resolveInner method in DeclareStyleableNameConverter also
// look up AndroidResourceUtil.findResourceFields, but that isn't semantically correct
// since "go to declaration" for the XML reference will jump to the derived generated
// R class field (and some other highlighting tests will fail).
//"values/attrs.xml:3:\n" +
//" <declare-styleable name=\"MyView\">\n" +
//" |~~~~~~ \n",
describeUsages(references));
}
public void testStyleableAttr() throws Throwable {
createManifest();
myFixture.copyFileToProject(BASE_PATH + "attrs.xml", "res/values/attrs.xml");
myFixture.copyFileToProject(BASE_PATH + "R_MyView.java", "src/p1/p2/R.java");
Collection<UsageInfo> references = findCodeUsages("MyView2.java", "src/p1/p2/MyView.java");
assertEquals("MyView.java:12:\n" +
" int attribute = R.attr.answer;\n" +
" |~~~~~~\n" +
"MyView.java:14:\n" +
" int answer = a.getInt(R.styleable.MyView_answer, 0);\n" +
" |~~~~~~~~~~~~~ \n" +
"R.java:46:\n" +
" <tr><td><code>{@link #MyView_answer p1.p2:answer}</code></td><td></td></tr>\n" +
" |~~~~~~~~~~~~~ \n" +
"R.java:48:\n" +
" @see #MyView_answer\n" +
" |~~~~~~~~~~~~\n" +
"R.java:54:\n" +
" <p>This symbol is the offset where the {@link p1.p2.R.attr#answer}\n" +
" |~~~~~~\n",
describeUsages(references));
}
public void testIdDeclarations() throws Throwable {
Collection<UsageInfo> references = findCodeUsages("fu12_layout.xml", "res/layout/f12_layout.xml");
assertEquals("layout/f12_layout.xml:26:\n" +
" android:layout_below=\"@+id/second\"\n" +
" |~~~~~~~~~~~\n" +
"layout/f12_layout.xml:27:\n" +
" android:labelFor=\"@id/second\"\n" +
" |~~~~~~~~~~\n",
describeUsages(references));
}
private static Collection<UsageInfo> findUsages(String fileName, final JavaCodeInsightTestFixture fixture, String newFilePath)
throws Throwable {
VirtualFile file = fixture.copyFileToProject(BASE_PATH + fileName, newFilePath);
fixture.configureFromExistingVirtualFile(file);
final UsageTarget[] targets = UsageTargetUtil.findUsageTargets(new DataProvider() {
@Override
public Object getData(@NonNls String dataId) {
return ((EditorEx)fixture.getEditor()).getDataContext().getData(dataId);
}
});
assert targets != null && targets.length > 0 && targets[0] instanceof PsiElementUsageTarget;
return fixture.findUsages(((PsiElementUsageTarget)targets[0]).getElement());
}
public static Collection<UsageInfo> findUsages(VirtualFile file, JavaCodeInsightTestFixture fixture) throws Exception {
fixture.configureFromExistingVirtualFile(file);
final PsiElement targetElement = TargetElementUtilBase
.findTargetElement(fixture.getEditor(),
TargetElementUtilBase.ELEMENT_NAME_ACCEPTED | TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED);
assert targetElement != null;
return fixture.findUsages(targetElement);
}
private static String describeUsages(Collection<UsageInfo> collection) {
assertNotNull(collection);
// Ensure stable output: sort in a predictable order
List<UsageInfo> usages = new ArrayList<UsageInfo>(collection);
Collections.sort(usages, new Comparator<UsageInfo>() {
@Override
public int compare(UsageInfo usageInfo1, UsageInfo usageInfo2) {
PsiFile file1 = usageInfo1.getFile();
PsiFile file2 = usageInfo2.getFile();
assertNotNull(file1);
assertNotNull(file2);
int delta = file1.getName().compareTo(file2.getName());
if (delta != 0) {
return delta;
}
VirtualFile virtualFile1 = file1.getVirtualFile();
VirtualFile virtualFile2 = file2.getVirtualFile();
if (virtualFile1 != null && virtualFile2 != null) {
delta = virtualFile1.getPath().compareTo(virtualFile2.getPath());
} else if (virtualFile1 != null) {
delta = -1;
} else {
delta = 1;
}
if (delta != 0) {
return delta;
}
return usageInfo1.getNavigationOffset() - usageInfo2.getNavigationOffset();
}
});
// Remove duplicates: For some of the tests we manually add an R class with values
// which duplicates the dynamically augmented (light) fields
List<UsageInfo> unique = Lists.newArrayListWithExpectedSize(usages.size());
PsiElement prev = null;
for (UsageInfo usage : usages) {
if (prev == null || usage.getElement() == null || !prev.isEquivalentTo(usage.getElement())) {
unique.add(usage);
}
prev = usage.getElement();
}
usages = unique;
// Create golden file output
StringBuilder sb = new StringBuilder();
for (UsageInfo usage : usages) {
PsiFile file = usage.getFile();
int navigationOffset = usage.getNavigationOffset();
Segment segment = usage.getSegment();
appendSourceDescription(sb, file, navigationOffset, segment);
}
return sb.toString();
}
}