diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java new file mode 100644 index 000000000..9c48ccdad --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/internal/editors/layout/refactoring/AdtProjectTest.java @@ -0,0 +1,495 @@ +/* + * 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.editors.layout.refactoring; + +import static com.android.SdkConstants.FD_RES; +import static com.android.SdkConstants.FD_RES_LAYOUT; +import static com.android.SdkConstants.FD_RES_VALUES; + +import com.android.ide.common.sdk.LoadStatus; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.AdtUtils; +import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; +import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs; +import com.android.ide.eclipse.adt.internal.project.BaseProjectHelper; +import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator; +import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState; +import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode; +import com.android.ide.eclipse.tests.SdkLoadingTestCase; +import com.android.sdklib.IAndroidTarget; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jface.operation.IRunnableContext; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.graphics.Point; +import org.eclipse.wst.sse.core.StructuredModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IModelManager; +import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; +import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@SuppressWarnings({"restriction", "javadoc"}) +public abstract class AdtProjectTest extends SdkLoadingTestCase { + private static final int TARGET_API_LEVEL = 16; + public static final String TEST_PROJECT_PACKAGE = "com.android.eclipse.tests"; //$NON-NLS-1$ + private static final long TESTS_START_TIME = System.currentTimeMillis(); + private static final String PROJECTNAME_PREFIX = "testproject-"; + + /** + * We don't stash the project used by each test case as a field such that test cases + * can share a single project instance (which is typically much faster). + * However, see {@link #getProjectName()} for exceptions to this sharing scheme. + */ + private static Map<String, IProject> sProjectMap = new HashMap<String, IProject>(); + + @Override + protected String getTestDataRelPath() { + return "eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/" + + "internal/editors/layout/refactoring/testdata"; + } + + @Override + protected InputStream getTestResource(String relativePath, boolean expectExists) { + String path = "testdata" + File.separator + relativePath; //$NON-NLS-1$ + InputStream stream = + AdtProjectTest.class.getResourceAsStream(path); + if (!expectExists && stream == null) { + return null; + } + return stream; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + // Prevent preview icon computation during plugin test to make test faster + if (AdtPlugin.getDefault() == null) { + fail("This test must be run as an Eclipse plugin test, not a plain JUnit test!"); + } + AdtPrefs.getPrefs().setPaletteModes("ICON_TEXT"); //$NON-NLS-1$ + + getProject(); + + Sdk current = Sdk.getCurrent(); + assertNotNull(current); + LoadStatus sdkStatus = AdtPlugin.getDefault().getSdkLoadStatus(); + assertSame(LoadStatus.LOADED, sdkStatus); + IAndroidTarget target = current.getTarget(getProject()); + IJavaProject javaProject = BaseProjectHelper.getJavaProject(getProject()); + assertNotNull(javaProject); + int iterations = 0; + while (true) { + if (iterations == 100) { + fail("Couldn't load target; ran out of time"); + } + LoadStatus status = current.checkAndLoadTargetData(target, javaProject); + if (status == LoadStatus.FAILED) { + fail("Couldn't load target " + target); + } + if (status != LoadStatus.LOADING) { + break; + } + Thread.sleep(250); + iterations++; + } + AndroidTargetData targetData = current.getTargetData(target); + assertNotNull(targetData); + LayoutDescriptors layoutDescriptors = targetData.getLayoutDescriptors(); + assertNotNull(layoutDescriptors); + List<ViewElementDescriptor> viewDescriptors = layoutDescriptors.getViewDescriptors(); + assertNotNull(viewDescriptors); + assertTrue(viewDescriptors.size() > 0); + List<ViewElementDescriptor> layoutParamDescriptors = + layoutDescriptors.getLayoutDescriptors(); + assertNotNull(layoutParamDescriptors); + assertTrue(layoutParamDescriptors.size() > 0); + } + + /** Set to true if the subclass test case should use a per-instance project rather + * than a shared project. This is needed by projects which modify the project in such + * a way that it affects what other tests see (for example, the quickfix resource creation + * tests will add in new resources, which the code completion tests will then list as + * possible matches if the code completion test is run after the quickfix test.) + * @return true to create a per-instance project instead of the default shared project + */ + protected boolean testCaseNeedsUniqueProject() { + return false; + } + + protected boolean testNeedsUniqueProject() { + return false; + } + + @Override + protected boolean validateSdk(IAndroidTarget target) { + // Not quite working yet. When enabled will make tests run faster. + //if (target.getVersion().getApiLevel() < TARGET_API_LEVEL) { + // return false; + //} + + return true; + } + + /** Returns a name to use for the project used in this test. Subclasses do not need to + * override this if they can share a project with others - which is the case if they do + * not modify the project in a way that does not affect other tests. For example + * the resource quickfix test will create new resources which affect what shows up + * in the code completion results, so the quickfix tests will override this method + * to produce a unique project for its own tests. + */ + private String getProjectName() { + if (testNeedsUniqueProject()) { + return PROJECTNAME_PREFIX + getClass().getSimpleName() + "-" + getName(); + } else if (testCaseNeedsUniqueProject()) { + return PROJECTNAME_PREFIX + getClass().getSimpleName(); + } else { + return PROJECTNAME_PREFIX + TESTS_START_TIME; + } + } + + protected IProject getProject() { + String projectName = getProjectName(); + IProject project = sProjectMap.get(projectName); + if (project == null) { + project = createProject(projectName); + assertNotNull(project); + sProjectMap.put(projectName, project); + } + if (!testCaseNeedsUniqueProject() && !testNeedsUniqueProject()) { + addCleanupDir(AdtUtils.getAbsolutePath(project).toFile()); + } + addCleanupDir(project.getFullPath().toFile()); + return project; + } + + protected IFile getTestDataFile(IProject project, String name) throws Exception { + return getTestDataFile(project, name, name); + } + + protected IFile getLayoutFile(IProject project, String name) throws Exception { + return getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name); + } + + protected IFile getValueFile(IProject project, String name) throws Exception { + return getTestDataFile(project, name, FD_RES + "/" + FD_RES_VALUES + "/" + name); + } + + protected IFile getTestDataFile(IProject project, String sourceName, + String destPath) throws Exception { + return getTestDataFile(project, sourceName, destPath, false); + } + + protected IFile getTestDataFile(IProject project, String sourceName, + String destPath, boolean overwrite) throws Exception { + String[] split = destPath.split("/"); //$NON-NLS-1$ + IContainer parent; + String name; + if (split.length == 1) { + parent = project; + name = destPath; + } else { + IFolder folder = project.getFolder(split[0]); + NullProgressMonitor monitor = new NullProgressMonitor(); + if (!folder.exists()) { + folder.create(true /* force */, true /* local */, monitor); + } + for (int i = 1, n = split.length; i < n -1; i++) { + IFolder subFolder = folder.getFolder(split[i]); + if (!subFolder.exists()) { + subFolder.create(true /* force */, true /* local */, monitor); + } + folder = subFolder; + } + name = split[split.length - 1]; + parent = folder; + } + IFile file = parent.getFile(new Path(name)); + if (overwrite && file.exists()) { + String currentContents = AdtPlugin.readFile(file); + String newContents = readTestFile(sourceName, true); + if (currentContents == null || !currentContents.equals(newContents)) { + file.delete(true, new NullProgressMonitor()); + } else { + return file; + } + } + if (!file.exists()) { + String xml = readTestFile(sourceName, true); + InputStream bstream = new ByteArrayInputStream(xml.getBytes("UTF-8")); //$NON-NLS-1$ + NullProgressMonitor monitor = new NullProgressMonitor(); + file.create(bstream, false /* force */, monitor); + } + + return file; + } + + protected IProject createProject(String name) { + IAndroidTarget target = null; + + IAndroidTarget[] targets = getSdk().getTargets(); + for (IAndroidTarget t : targets) { + if (!t.isPlatform()) { + continue; + } + if (t.getVersion().getApiLevel() >= TARGET_API_LEVEL) { + target = t; + break; + } + } + assertNotNull(target); + + IRunnableContext context = new IRunnableContext() { + @Override + public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) + throws InvocationTargetException, InterruptedException { + runnable.run(new NullProgressMonitor()); + } + }; + NewProjectWizardState state = new NewProjectWizardState(Mode.ANY); + state.projectName = name; + state.target = target; + state.packageName = TEST_PROJECT_PACKAGE; + state.activityName = name; + state.applicationName = name; + state.createActivity = false; + state.useDefaultLocation = true; + if (getMinSdk() != -1) { + state.minSdk = Integer.toString(getMinSdk()); + } + + NewProjectCreator creator = new NewProjectCreator(state, context); + creator.createAndroidProjects(); + return validateProjectExists(name); + } + + protected int getMinSdk() { + return -1; + } + + public void createTestProject() { + IAndroidTarget target = null; + + IAndroidTarget[] targets = getSdk().getTargets(); + for (IAndroidTarget t : targets) { + if (t.getVersion().getApiLevel() >= TARGET_API_LEVEL) { + target = t; + break; + } + } + assertNotNull(target); + } + + protected static IProject validateProjectExists(String name) { + IProject iproject = getProject(name); + assertTrue(String.format("%s project not created", name), iproject.exists()); + assertTrue(String.format("%s project not opened", name), iproject.isOpen()); + return iproject; + } + + private static IProject getProject(String name) { + IProject iproject = ResourcesPlugin.getWorkspace().getRoot().getProject(name); + return iproject; + } + + protected int getCaretOffset(IFile file, String caretLocation) { + assertTrue(caretLocation, caretLocation.contains("^")); + + String fileContent = AdtPlugin.readFile(file); + return getCaretOffset(fileContent, caretLocation); + } + + /** + * If the given caret location string contains a selection range, select that range in + * the given viewer + * + * @param viewer the viewer to contain the selection + * @param caretLocation the location string + */ + protected int updateCaret(ISourceViewer viewer, String caretLocation) { + assertTrue(caretLocation, caretLocation.contains("^")); //$NON-NLS-1$ + + int caretDelta = caretLocation.indexOf("^"); //$NON-NLS-1$ + assertTrue(caretLocation, caretDelta != -1); + String text = viewer.getTextWidget().getText(); + + int length = 0; + + // String around caret/range without the range and caret marker characters + String caretContext; + + if (caretLocation.contains("[^")) { //$NON-NLS-1$ + caretDelta--; + assertTrue(caretLocation, caretLocation.startsWith("[^", caretDelta)); //$NON-NLS-1$ + + int caretRangeEnd = caretLocation.indexOf(']', caretDelta + 2); + assertTrue(caretLocation, caretRangeEnd != -1); + length = caretRangeEnd - caretDelta - 2; + assertTrue(length > 0); + caretContext = caretLocation.substring(0, caretDelta) + + caretLocation.substring(caretDelta + 2, caretRangeEnd) + + caretLocation.substring(caretRangeEnd + 1); + } else { + caretContext = caretLocation.substring(0, caretDelta) + + caretLocation.substring(caretDelta + 1); // +1: skip "^" + } + + int caretContextIndex = text.indexOf(caretContext); + + assertTrue("Caret content " + caretContext + " not found in file", + caretContextIndex != -1); + + int offset = caretContextIndex + caretDelta; + viewer.setSelectedRange(offset, length); + + return offset; + } + + protected String addSelection(String newFileContents, Point selectedRange) { + int selectionBegin = selectedRange.x; + int selectionEnd = selectionBegin + selectedRange.y; + return addSelection(newFileContents, selectionBegin, selectionEnd); + } + + @Override + protected String removeSessionData(String data) { + data = super.removeSessionData(data); + if (getProject() != null) { + data = data.replace(getProject().getName(), "PROJECTNAME"); + } + + return data; + } + + public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) { + if (hasChildren) { + return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0], + new AttributeDescriptor[0], new ElementDescriptor[1], false); + } else { + return new ViewElementDescriptor(name, fqn); + } + } + + public static UiViewElementNode createNode(UiViewElementNode parent, String fqn, + boolean hasChildren) { + String name = fqn.substring(fqn.lastIndexOf('.') + 1); + ViewElementDescriptor descriptor = createDesc(name, fqn, hasChildren); + if (parent == null) { + // All node hierarchies should be wrapped inside a document node at the root + parent = new UiViewElementNode(createDesc("doc", "doc", true)); + } + return (UiViewElementNode) parent.appendNewUiChild(descriptor); + } + + public static UiViewElementNode createNode(String fqn, boolean hasChildren) { + return createNode(null, fqn, hasChildren); + } + + /** Special editor context set on the model to be rendered */ + protected static class TestLayoutEditorDelegate extends LayoutEditorDelegate { + + public TestLayoutEditorDelegate( + IFile file, + IStructuredDocument structuredDocument, + UiDocumentNode uiRootNode) { + super(new TestAndroidXmlCommonEditor(file, structuredDocument, uiRootNode)); + } + + static class TestAndroidXmlCommonEditor extends CommonXmlEditor { + + private final IFile mFile; + private final IStructuredDocument mStructuredDocument; + private UiDocumentNode mUiRootNode; + + TestAndroidXmlCommonEditor( + IFile file, + IStructuredDocument structuredDocument, + UiDocumentNode uiRootNode) { + mFile = file; + mStructuredDocument = structuredDocument; + mUiRootNode = uiRootNode; + } + + @Override + public IFile getInputFile() { + return mFile; + } + + @Override + public IProject getProject() { + return mFile.getProject(); + } + + @Override + public IStructuredDocument getStructuredDocument() { + return mStructuredDocument; + } + + @Override + public UiDocumentNode getUiRootNode() { + return mUiRootNode; + } + + @Override + public void editorDirtyStateChanged() { + } + + @Override + public IStructuredModel getModelForRead() { + IModelManager mm = StructuredModelManager.getModelManager(); + if (mm != null) { + try { + return mm.getModelForRead(mFile); + } catch (Exception e) { + fail(e.toString()); + } + } + + return null; + } + } + } + + public void testDummy() { + // This class contains shared test functionality for testcase subclasses, + // but without an actual test in the class JUnit complains (even if we make + // it abstract) + } +} |