/* * Copyright (C) 2011 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.build; import static com.android.SdkConstants.FD_RES; import static com.android.SdkConstants.FD_RES_COLOR; import static com.android.SdkConstants.FD_RES_LAYOUT; import com.android.ide.eclipse.adt.AdtConstants; import com.android.ide.eclipse.adt.AdtPlugin; import com.android.ide.eclipse.adt.AdtUtils; import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.Path; import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext; import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.swt.graphics.Point; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.IMarkerResolution; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.ide.IDE; import org.eclipse.ui.part.FileEditorInput; import java.io.File; import java.util.ArrayList; import java.util.List; public class AaptQuickFixTest extends AdtProjectTest { @Override protected boolean testCaseNeedsUniqueProject() { // Make a separate test project for this test such that we don't pollute code assist // tests with our new resources return true; } public void testQuickFix1() throws Exception { // Test adding a value into an existing file (res/values/strings.xml) checkResourceFix("quickfix1.xml", "android:text=\"@string/firs^tstring\"", "res/values/strings.xml"); } public void testQuickFix2() throws Exception { // Test adding a value into a new file (res/values/dimens.xml, will be created) checkResourceFix("quickfix1.xml", "android:layout_width=\"@dimen/^testdimen\"", "res/values/dimens.xml"); } public void testQuickFix3() throws Exception { // Test adding a file based resource (uses new file wizard machinery) checkResourceFix("quickfix1.xml", "layout=\"@layout/^testlayout\"", "res/layout/testlayout.xml"); } public void testQuickFix4() throws Exception { // Test adding a value into a new file (res/values/dimens.xml, will be created) checkNamespaceFix("quickfix2.xml", "<c^olor"); } private void checkResourceFix(String name, String caretLocation, String expectedNewPath) throws Exception { IProject project = getProject(); IFile file = getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name); // Determine the offset final int offset = getCaretOffset(file, caretLocation); String osRoot = project.getLocation().toOSString(); List<String> errors = new ArrayList<String>(); String fileRelativePath = file.getProjectRelativePath().toPortableString(); String filePath = osRoot + File.separator + fileRelativePath; // Run AaptParser such that markers are added... // When debugging these tests, the project gets a chance to build itself so // the real aapt errors are there. But when the test is run directly, aapt has // not yet run. I tried waiting for the build (using the code in SampleProjectTest) // but this had various adverse effects (exception popups from the Eclipse debugger // etc) so instead this test just hardcodes the aapt errors that should be // observed on quickfix1.xml. assertEquals("Unit test is hardcoded to errors for quickfix1.xml", "quickfix1.xml", name); errors.add(filePath + ":7: error: Error: No resource found that matches the given name" + " (at 'text' with value '@string/firststring')."); errors.add(filePath + ":7: error: Error: No resource found that matches the given name" + " (at 'layout_width' with value '@dimen/testdimen')."); errors.add(filePath + ":13: error: Error: No resource found that matches the given name" + " (at 'layout' with value '@layout/testlayout')."); AaptParser.parseOutput(errors, project); AaptQuickFix aaptQuickFix = new AaptQuickFix(); // Open file IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); assertNotNull(page); IEditorPart editor = IDE.openEditor(page, file); assertTrue(editor instanceof AndroidXmlEditor); AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor; final ISourceViewer viewer = layoutEditor.getStructuredSourceViewer(); // Test marker resolution. IMarker[] markers = file.findMarkers(AdtConstants.MARKER_AAPT_COMPILE, true, IResource.DEPTH_ZERO); for (IMarker marker : markers) { int start = marker.getAttribute(IMarker.CHAR_START, 0); int end = marker.getAttribute(IMarker.CHAR_END, 0); if (offset >= start && offset <= end) { // Found the target marker. Now check the marker resolution of it. assertTrue(aaptQuickFix.hasResolutions(marker)); IMarkerResolution[] resolutions = aaptQuickFix.getResolutions(marker); assertNotNull(resolutions); assertEquals(1, resolutions.length); IMarkerResolution resolution = resolutions[0]; assertNotNull(resolution); assertTrue(resolution.getLabel().contains("Create resource")); // Not running marker yet -- if we create the files here they already // exist when the quick assist code runs. (The quick fix and the quick assist // mostly share code for the implementation anyway.) //resolution.run(marker); break; } } // Next test quick assist. IQuickAssistInvocationContext invocationContext = new IQuickAssistInvocationContext() { @Override public int getLength() { return 0; } @Override public int getOffset() { return offset; } @Override public ISourceViewer getSourceViewer() { return viewer; } }; ICompletionProposal[] proposals = aaptQuickFix .computeQuickAssistProposals(invocationContext); assertNotNull(proposals); assertTrue(proposals.length == 1); ICompletionProposal proposal = proposals[0]; assertNotNull(proposal.getAdditionalProposalInfo()); assertNotNull(proposal.getImage()); assertTrue(proposal.getDisplayString().contains("Create resource")); IDocument document = new Document(); String fileContent = AdtPlugin.readFile(file); document.set(fileContent); // Apply quick fix proposal.apply(document); IPath path = new Path(expectedNewPath); IFile newFile = project.getFile(path); assertNotNull(path.toPortableString(), newFile); // Ensure that the newly created file was opened IEditorPart currentFile = AdtUtils.getActiveEditor(); assertEquals(newFile.getProjectRelativePath(), ((FileEditorInput) currentFile.getEditorInput()).getFile().getProjectRelativePath()); // Look up caret offset assertTrue(currentFile != null ? currentFile.getClass().getName() : "null", currentFile instanceof AndroidXmlEditor); AndroidXmlEditor newEditor = (AndroidXmlEditor) currentFile; ISourceViewer newViewer = newEditor.getStructuredSourceViewer(); Point selectedRange = newViewer.getSelectedRange(); String newFileContents = AdtPlugin.readFile(newFile); // Insert selection markers -- [ ] for the selection range, ^ for the caret String newFileWithCaret = addSelection(newFileContents, selectedRange); newFileWithCaret = removeSessionData(newFileWithCaret); assertEqualsGolden(name, newFileWithCaret); } private void checkNamespaceFix(String name, String caretLocation) throws Exception { IProject project = getProject(); IFile file = getTestDataFile(project, name, FD_RES + "/" + FD_RES_COLOR + "/" + name); // Determine the offset final int offset = getCaretOffset(file, caretLocation); String osRoot = project.getLocation().toOSString(); List<String> errors = new ArrayList<String>(); String fileRelativePath = file.getProjectRelativePath().toPortableString(); String filePath = osRoot + File.separator + fileRelativePath; assertEquals("Unit test is hardcoded to errors for quickfix2.xml", "quickfix2.xml", name); errors.add(filePath + ":5: error: Error parsing XML: unbound prefix"); AaptParser.parseOutput(errors, project); AaptQuickFix aaptQuickFix = new AaptQuickFix(); // Open file IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage(); assertNotNull(page); IEditorPart editor = IDE.openEditor(page, file); assertTrue(editor instanceof AndroidXmlEditor); AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor; final ISourceViewer viewer = layoutEditor.getStructuredSourceViewer(); // Test marker resolution. IMarker[] markers = file.findMarkers(AdtConstants.MARKER_AAPT_COMPILE, true, IResource.DEPTH_ZERO); assertEquals(1, markers.length); IMarker marker = markers[0]; // Found the target marker. Now check the marker resolution of it. assertTrue(aaptQuickFix.hasResolutions(marker)); IMarkerResolution[] resolutions = aaptQuickFix.getResolutions(marker); assertNotNull(resolutions); assertEquals(1, resolutions.length); IMarkerResolution resolution = resolutions[0]; assertNotNull(resolution); assertTrue(resolution.getLabel().contains("Insert namespace")); // Next test quick assist. IQuickAssistInvocationContext invocationContext = new IQuickAssistInvocationContext() { @Override public int getLength() { return 0; } @Override public int getOffset() { return offset; } @Override public ISourceViewer getSourceViewer() { return viewer; } }; ICompletionProposal[] proposals = aaptQuickFix .computeQuickAssistProposals(invocationContext); assertNotNull(proposals); assertTrue(proposals.length == 1); ICompletionProposal proposal = proposals[0]; assertNotNull(proposal.getAdditionalProposalInfo()); assertNotNull(proposal.getImage()); assertTrue(proposal.getDisplayString().contains("Insert namespace")); // Open the file to ensure we can get an XML model with getExistingModelForEdit: AdtPlugin.openFile(file, null); IEditorPart newEditor = AdtUtils.getActiveEditor(); assertTrue(newEditor instanceof AndroidXmlEditor); AndroidXmlEditor xmlEditor = (AndroidXmlEditor) newEditor; IDocument document = xmlEditor.getStructuredSourceViewer().getDocument(); // Apply quick fix String before = document.get(); proposal.apply(document); String after = document.get(); String diff = getDiff(before, after); assertEqualsGolden(name, diff); } }