| /* |
| * 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) |
| } |
| } |