diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java | 320 |
1 files changed, 320 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java new file mode 100644 index 000000000..20aae846b --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/HyperlinksTest.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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.ide.eclipse.adt.internal.editors; + +import static com.android.SdkConstants.FD_SOURCES; + +import com.android.ide.common.resources.ResourceFile; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.editors.Hyperlinks.ResourceLink; +import com.android.ide.eclipse.adt.internal.editors.Hyperlinks.XmlResolver; +import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest; + +import org.eclipse.core.resources.IFile; +import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.hyperlink.IHyperlink; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.graphics.Point; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.internal.ErrorEditorPart; +import org.eclipse.ui.internal.browser.WebBrowserEditor; +import org.eclipse.wst.sse.ui.StructuredTextEditor; +import org.eclipse.wst.sse.ui.internal.StructuredTextViewer; +import org.eclipse.wst.xml.ui.internal.tabletree.XMLMultiPageEditorPart; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +@SuppressWarnings({ + "restriction", "javadoc" +}) +public class HyperlinksTest extends AdtProjectTest { + @Override + protected boolean testCaseNeedsUniqueProject() { + return true; + } + + public void testFqnRegexp() throws Exception { + assertTrue(Hyperlinks.isViewClassName("com.android.Foo")); + assertTrue(Hyperlinks.isViewClassName("com.android.pk_g.Foo_Bar1")); + assertTrue(Hyperlinks.isViewClassName("com.android.Foo$Inner")); + + // Should we allow non-standard packages and class names? + // For now, we're allowing it -- see how this works out in practice. + //assertFalse(XmlHyperlinkResolver.isViewClassName("Foo.bar")); + assertTrue(Hyperlinks.isViewClassName("Foo.bar")); + + assertFalse(Hyperlinks.isViewClassName("LinearLayout")); + assertFalse(Hyperlinks.isViewClassName(".")); + assertFalse(Hyperlinks.isViewClassName(".F")); + assertFalse(Hyperlinks.isViewClassName("f.")); + assertFalse(Hyperlinks.isViewClassName("Foo")); + assertFalse(Hyperlinks.isViewClassName("com.android.1Foo")); + assertFalse(Hyperlinks.isViewClassName("1com.Foo")); + } + + public void testNavigate1() throws Exception { + // Check navigating to a local resource + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "android:text=\"@string/app^_name\""); + } + + public void testNavigate2() throws Exception { + // Check navigating to a framework resource + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "marginLeft=\"@android:dimen/app_ico^n_size\""); + } + + public void testNavigate3() throws Exception { + // Check navigating to a style + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "style=\"@android:style/Widget.B^utton\""); + } + + public void testNavigate4() throws Exception { + // Check navigating to resource with many resolutions + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "android:text=\"@android:st^ring/ok\""); + } + + public void testNavigate5() throws Exception { + // Check navigating to styles + checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml", + "parent=\"android:Theme.Li^ght\">"); + } + + public void testNavigate6() throws Exception { + // Check navigating to a portion of a style (this should pick android:Theme, not + // android:Theme.Light + checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml", + "parent=\"android:The^me.Light\">"); + } + + public void testNavigate7() throws Exception { + // Check navigating to a resource inside text content + checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml", + "popupBackground\">@android:drawable/spinner_dr^opdown_background</item>"); + } + + public void testNavigate8() throws Exception { + // Check navigating to a resource inside text content where there is space around + // the URL + checkXmlNavigation("navigationstyles.xml", "res/values/navigationstyles.xml", + "colorBackground\"> @color/cust^om_theme_color </item>"); + } + + public void testNavigate9a() throws Exception { + // Check navigating to a an activity + checkXmlNavigation("manifest.xml", "AndroidManifest.xml", + "<activity android:name=\".Test^Activity\""); + } + + /* Not yet implemented + public void testNavigate9b() throws Exception { + // Check navigating to a an activity - clicking on the activity element should + // work too + checkXmlNavigation("manifest.xml", "AndroidManifest.xml", + "<acti^vity android:name=\".TestActivity\""); + } + */ + + public void testNavigate10() throws Exception { + // Check navigating to a permission + checkXmlNavigation("manifest.xml", "AndroidManifest.xml", + "<uses-permission android:name=\"android.permission.AC^CESS_NETWORK_STATE\" />"); + } + + public void testNavigate11a() throws Exception { + // Check navigating to an intent + checkXmlNavigation("manifest.xml", "AndroidManifest.xml", + "<action android:name=\"android.intent.ac^tion.MAIN\" />"); + } + + public void testNavigate11g() throws Exception { + // Check navigating to an intent + checkXmlNavigation("manifest.xml", "AndroidManifest.xml", + "<category android:name=\"android.intent.category.LA^UNCHER\" />"); + } + + public void testNavigate12() throws Exception { + // Check navigating to a custom view class + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "<my.Cust^omView></my.CustomView>"); + } + + public void testNavigate13() throws Exception { + // Check jumping to classes pointed to by fragments + + getTestDataFile(getProject(), "TestFragment.java.txt", + FD_SOURCES + "/" + TEST_PROJECT_PACKAGE.replace('.', '/') + "/TestFragment.java"); + checkXmlNavigation("fragmentlayout.xml", "res/layout/fragmentlayout.xml", + "android:name=\"com.android.ecl^ipse.tests.TestFragment\""); + } + + public void testNavigate14() throws Exception { + // Check jumping to classes pointed to by fragments + + getTestDataFile(getProject(), "TestFragment.java.txt", + FD_SOURCES + "/" + TEST_PROJECT_PACKAGE.replace('.', '/') + "/TestFragment.java"); + checkXmlNavigation("fragmentlayout.xml", "res/layout/fragmentlayout.xml", + "class=\"com.and^roid.eclipse.tests.TestFragment\""); + } + + public void testNavigate15() throws Exception { + // Check navigating to a theme resource + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "?android:attr/alert^DialogStyle"); + } + + public void testNavigate16() throws Exception { + // Check navigating to a theme resource + checkXmlNavigation("navigation1.xml", "res/layout/navigation1.xml", + "?android:alert^DialogStyle"); + } + + // Left to test: + // onClick handling + // class attributes + // Test that the correct file is actually opened! + + private void checkXmlNavigation(String basename, String targetPath, + String caretLocation) throws Exception { + IFile file = getTestDataFile(getProject(), basename, targetPath, true); + + // Determine the offset + int offset = getCaretOffset(file, caretLocation); + + // Open file + IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); + assertNotNull(page); + IEditorPart editor = IDE.openEditor(page, file); + assertTrue(editor.getClass().getName(), editor instanceof AndroidXmlEditor); + AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor; + ISourceViewer viewer = layoutEditor.getStructuredSourceViewer(); + + XmlResolver resolver = new Hyperlinks.XmlResolver(); + IHyperlink[] links = resolver.detectHyperlinks(viewer, new Region(offset, 0), true); + assertNotNull(links); + + StringBuilder sb = new StringBuilder(1000); + sb.append("Go To Declaration in " + basename + " for " + caretLocation + ":\n"); + for (IHyperlink link : links) { + sb.append(link.getHyperlinkText()); + sb.append(" : "); + sb.append(" ["); + IRegion region = link.getHyperlinkRegion(); + sb.append(viewer.getDocument().get(region.getOffset(), region.getLength())); + sb.append("]"); + if (link instanceof Hyperlinks.ResourceLink) { + Hyperlinks.ResourceLink resourceLink = (ResourceLink) link; + sb.append("\n "); + ResourceFile resourceFile = resourceLink.getFile(); + String desc = resourceFile.toString(); + desc = desc.replace('\\', '/'); + // For files in the SDK folder, strip out file prefix + int dataRes = desc.indexOf("data/res"); + if (dataRes != -1) { + desc = desc.substring(dataRes); + } + desc = removeSessionData(desc); + sb.append(desc); + } + sb.append('\n'); + } + + // Open the first link + IHyperlink link = links[0]; + link.open(); + IEditorPart newEditor = AdtUtils.getActiveEditor(); + // Ensure that this isn't an invalid file (e.g. opening the SDK platform files + // with incorrect content binding could cause this) + assertTrue(!(newEditor instanceof ErrorEditorPart)); + + IDocument document = null; + Point selection = null; + + if (newEditor instanceof AndroidXmlEditor) { + AndroidXmlEditor xmlEditor = (AndroidXmlEditor) newEditor; + document = xmlEditor.getStructuredSourceViewer().getDocument(); + selection = xmlEditor.getStructuredSourceViewer().getSelectedRange(); + } else if (newEditor instanceof XMLMultiPageEditorPart) { + XMLMultiPageEditorPart xmlEditor = (XMLMultiPageEditorPart) newEditor; + Field field = xmlEditor.getClass().getDeclaredField("fTextEditor"); + field.setAccessible(true); + StructuredTextEditor ste = (StructuredTextEditor) field.get(newEditor); + if (ste == null) { + Method method = xmlEditor.getClass().getMethod("getTextEditor", new Class[0]); + ste = (StructuredTextEditor) method.invoke(newEditor, new Object[0]); + } + StructuredTextViewer textViewer = ste.getTextViewer(); + document = textViewer.getDocument(); + selection = textViewer.getSelectedRange(); + } else if (newEditor instanceof WebBrowserEditor) { + WebBrowserEditor browser = (WebBrowserEditor) newEditor; + Field field = browser.getClass().getDeclaredField("initialURL"); + field.setAccessible(true); + String initialUrl = (String) field.get(newEditor); + int index = initialUrl.indexOf("reference"); + if (index != -1) { + initialUrl = initialUrl.substring(index); + } + initialUrl = initialUrl.replace('\\', '/'); + sb.append("\n\nAfter open, a browser is shown with this URL:\n"); + sb.append(" "); + sb.append(initialUrl); + sb.append("\n"); + } else if (newEditor instanceof JavaEditor) { + JavaEditor javaEditor = (JavaEditor) newEditor; + document = javaEditor.getDocumentProvider().getDocument(javaEditor.getEditorInput()); + IRegion range = javaEditor.getHighlightRange(); + selection = new Point(range.getOffset(), range.getLength()); + } else { + fail("Unhandled editor type: " + newEditor.getClass().getName()); + return; + } + + if (document != null && selection != null) { + int lineStart = document.getLineInformationOfOffset(selection.x).getOffset(); + IRegion lineEndInfo = document.getLineInformationOfOffset(selection.x + selection.y); + int lineEnd = lineEndInfo.getOffset() + lineEndInfo.getLength(); + String text = document.get(lineStart, lineEnd - lineStart); + int selectionStart = selection.x - lineStart; + int selectionEnd = selectionStart + selection.y; + if (selectionEnd > selectionStart) { + // Selection range + text = text.substring(0, selectionStart) + "[^" + + text.substring(selectionStart, selectionEnd) + "]" + + text.substring(selectionEnd); + } else { + text = text.substring(0, selectionStart) + "^" + + text.substring(selectionStart); + } + text = removeSessionData(text); + + sb.append("\n\nAfter open, the selected text is:\n"); + sb.append(text); + sb.append("\n"); + } + + assertEqualsGolden(basename, sb.toString(), "txt"); + } +} |