Home | History | Annotate | Download | only in refactoring
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
     17 
     18 import static com.android.SdkConstants.FD_RES;
     19 import static com.android.SdkConstants.FD_RES_LAYOUT;
     20 import static com.android.SdkConstants.FD_RES_VALUES;
     21 
     22 import com.android.SdkConstants;
     23 import com.android.ide.eclipse.adt.AdtPlugin;
     24 import com.android.ide.eclipse.adt.internal.editors.common.CommonXmlEditor;
     25 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
     26 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
     27 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditorDelegate;
     28 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
     29 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
     30 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
     31 import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
     32 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectCreator;
     33 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState;
     34 import com.android.ide.eclipse.adt.internal.wizards.newproject.NewProjectWizardState.Mode;
     35 import com.android.ide.eclipse.tests.SdkTestCase;
     36 import com.android.sdklib.IAndroidTarget;
     37 
     38 import org.eclipse.core.resources.IContainer;
     39 import org.eclipse.core.resources.IFile;
     40 import org.eclipse.core.resources.IFolder;
     41 import org.eclipse.core.resources.IProject;
     42 import org.eclipse.core.resources.ResourcesPlugin;
     43 import org.eclipse.core.runtime.NullProgressMonitor;
     44 import org.eclipse.core.runtime.Path;
     45 import org.eclipse.jface.operation.IRunnableContext;
     46 import org.eclipse.jface.operation.IRunnableWithProgress;
     47 import org.eclipse.jface.text.source.ISourceViewer;
     48 import org.eclipse.swt.graphics.Point;
     49 import org.eclipse.wst.sse.core.StructuredModelManager;
     50 import org.eclipse.wst.sse.core.internal.provisional.IModelManager;
     51 import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
     52 import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
     53 
     54 import java.io.BufferedReader;
     55 import java.io.ByteArrayInputStream;
     56 import java.io.File;
     57 import java.io.InputStream;
     58 import java.io.InputStreamReader;
     59 import java.lang.reflect.InvocationTargetException;
     60 import java.util.Calendar;
     61 import java.util.HashMap;
     62 import java.util.Map;
     63 
     64 @SuppressWarnings({"restriction", "javadoc"})
     65 public class AdtProjectTest extends SdkTestCase {
     66     private static final int TARGET_API_LEVEL = 12;
     67     public static final String TEST_PROJECT_PACKAGE = "com.android.eclipse.tests"; //$NON-NLS-1$
     68 
     69     /** Update golden files if different from the actual results */
     70     private static final boolean UPDATE_DIFFERENT_FILES = false;
     71     /** Create golden files if missing */
     72     private static final boolean UPDATE_MISSING_FILES = true;
     73     private static final String TEST_DATA_REL_PATH =
     74         "eclipse/plugins/com.android.ide.eclipse.tests/src/com/android/ide/eclipse/adt/"
     75         + "internal/editors/layout/refactoring/testdata";
     76     private static final String PROJECTNAME_PREFIX = "testproject-";
     77     private static final long TESTS_START_TIME = System.currentTimeMillis();
     78     private static File sTempDir = null;
     79 
     80     /**
     81      * We don't stash the project used by each test case as a field such that test cases
     82      * can share a single project instance (which is typically much faster).
     83      * However, see {@link #getProjectName()} for exceptions to this sharing scheme.
     84      */
     85     private static Map<String, IProject> sProjectMap = new HashMap<String, IProject>();
     86 
     87     @Override
     88     protected void setUp() throws Exception {
     89         super.setUp();
     90 
     91         // Prevent preview icon computation during plugin test to make test faster
     92         if (AdtPlugin.getDefault() == null) {
     93             fail("This test must be run as an Eclipse plugin test, not a plain JUnit test!");
     94         }
     95         AdtPrefs.getPrefs().setPaletteModes("ICON_TEXT"); //$NON-NLS-1$
     96 
     97         getProject();
     98     }
     99 
    100     /** Set to true if the subclass test case should use a per-instance project rather
    101      * than a shared project. This is needed by projects which modify the project in such
    102      * a way that it affects what other tests see (for example, the quickfix resource creation
    103      * tests will add in new resources, which the code completion tests will then list as
    104      * possible matches if the code completion test is run after the quickfix test.)
    105      * @return true to create a per-instance project instead of the default shared project
    106      */
    107     protected boolean testCaseNeedsUniqueProject() {
    108         return false;
    109     }
    110 
    111     protected boolean testNeedsUniqueProject() {
    112         return false;
    113     }
    114 
    115     @Override
    116     protected boolean validateSdk(IAndroidTarget target) {
    117         // Not quite working yet. When enabled will make tests run faster.
    118         //if (target.getVersion().getApiLevel() < TARGET_API_LEVEL) {
    119         //    return false;
    120         //}
    121 
    122         return true;
    123     }
    124 
    125     /** Returns a name to use for the project used in this test. Subclasses do not need to
    126      * override this if they can share a project with others - which is the case if they do
    127      * not modify the project in a way that does not affect other tests. For example
    128      * the resource quickfix test will create new resources which affect what shows up
    129      * in the code completion results, so the quickfix tests will override this method
    130      * to produce a unique project for its own tests.
    131      */
    132     private String getProjectName() {
    133         if (testNeedsUniqueProject()) {
    134             return PROJECTNAME_PREFIX + getClass().getSimpleName() + "-" + getName();
    135         } else if (testCaseNeedsUniqueProject()) {
    136             return PROJECTNAME_PREFIX + getClass().getSimpleName();
    137         } else {
    138             return PROJECTNAME_PREFIX + TESTS_START_TIME;
    139         }
    140     }
    141 
    142     protected IProject getProject() {
    143         String projectName = getProjectName();
    144         IProject project = sProjectMap.get(projectName);
    145         if (project == null) {
    146             project = createProject(projectName);
    147             assertNotNull(project);
    148             sProjectMap.put(projectName, project);
    149         }
    150 
    151         return project;
    152     }
    153 
    154     protected IFile getTestDataFile(IProject project, String name) throws Exception {
    155         return getTestDataFile(project, name, name);
    156     }
    157 
    158     protected IFile getLayoutFile(IProject project, String name) throws Exception {
    159         return getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name);
    160     }
    161 
    162     protected IFile getValueFile(IProject project, String name) throws Exception {
    163         return getTestDataFile(project, name, FD_RES + "/" + FD_RES_VALUES + "/" + name);
    164     }
    165 
    166     protected IFile getTestDataFile(IProject project, String sourceName,
    167             String destPath) throws Exception {
    168         return getTestDataFile(project, sourceName, destPath, false);
    169     }
    170 
    171     protected IFile getTestDataFile(IProject project, String sourceName,
    172             String destPath, boolean overwrite) throws Exception {
    173         String[] split = destPath.split("/"); //$NON-NLS-1$
    174         IContainer parent;
    175         String name;
    176         if (split.length == 1) {
    177             parent = project;
    178             name = destPath;
    179         } else {
    180             IFolder folder = project.getFolder(split[0]);
    181             NullProgressMonitor monitor = new NullProgressMonitor();
    182             if (!folder.exists()) {
    183                 folder.create(true /* force */, true /* local */, monitor);
    184             }
    185             for (int i = 1, n = split.length; i < n -1; i++) {
    186                 IFolder subFolder = folder.getFolder(split[i]);
    187                 if (!subFolder.exists()) {
    188                     subFolder.create(true /* force */, true /* local */, monitor);
    189                 }
    190                 folder = subFolder;
    191             }
    192             name = split[split.length - 1];
    193             parent = folder;
    194         }
    195         IFile file = parent.getFile(new Path(name));
    196         if (overwrite && file.exists()) {
    197             String currentContents = AdtPlugin.readFile(file);
    198             String newContents = readTestFile(sourceName, true);
    199             if (currentContents == null || !currentContents.equals(newContents)) {
    200                 file.delete(true, new NullProgressMonitor());
    201             } else {
    202                 return file;
    203             }
    204         }
    205         if (!file.exists()) {
    206             String xml = readTestFile(sourceName, true);
    207             InputStream bstream = new ByteArrayInputStream(xml.getBytes("UTF-8")); //$NON-NLS-1$
    208             NullProgressMonitor monitor = new NullProgressMonitor();
    209             file.create(bstream, false /* force */, monitor);
    210         }
    211 
    212         return file;
    213     }
    214 
    215     protected IProject createProject(String name) {
    216         IAndroidTarget target = null;
    217 
    218         IAndroidTarget[] targets = getSdk().getTargets();
    219         for (IAndroidTarget t : targets) {
    220             if (t.getVersion().getApiLevel() >= TARGET_API_LEVEL) {
    221                 target = t;
    222                 break;
    223             }
    224         }
    225         assertNotNull(target);
    226 
    227 
    228         IRunnableContext context = new IRunnableContext() {
    229             @Override
    230             public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable)
    231                     throws InvocationTargetException, InterruptedException {
    232                 runnable.run(new NullProgressMonitor());
    233             }
    234         };
    235         NewProjectWizardState state = new NewProjectWizardState(Mode.ANY);
    236         state.projectName = name;
    237         state.target = target;
    238         state.packageName = TEST_PROJECT_PACKAGE;
    239         state.activityName = name;
    240         state.applicationName = name;
    241         state.createActivity = false;
    242         state.useDefaultLocation = true;
    243 
    244         NewProjectCreator creator = new NewProjectCreator(state, context);
    245         creator.createAndroidProjects();
    246         return validateProjectExists(name);
    247     }
    248 
    249     public void createTestProject() {
    250         IAndroidTarget target = null;
    251 
    252         IAndroidTarget[] targets = getSdk().getTargets();
    253         for (IAndroidTarget t : targets) {
    254             if (t.getVersion().getApiLevel() >= TARGET_API_LEVEL) {
    255                 target = t;
    256                 break;
    257             }
    258         }
    259         assertNotNull(target);
    260     }
    261 
    262     private static IProject validateProjectExists(String name) {
    263         IProject iproject = getProject(name);
    264         assertTrue(String.format("%s project not created", name), iproject.exists());
    265         assertTrue(String.format("%s project not opened", name), iproject.isOpen());
    266         return iproject;
    267     }
    268 
    269     private static IProject getProject(String name) {
    270         IProject iproject = ResourcesPlugin.getWorkspace().getRoot()
    271                 .getProject(name);
    272         return iproject;
    273     }
    274 
    275     protected int getCaretOffset(IFile file, String caretLocation) {
    276         assertTrue(caretLocation, caretLocation.contains("^"));
    277 
    278         String fileContent = AdtPlugin.readFile(file);
    279         return getCaretOffset(fileContent, caretLocation);
    280     }
    281 
    282     protected int getCaretOffset(String fileContent, String caretLocation) {
    283         assertTrue(caretLocation, caretLocation.contains("^")); //$NON-NLS-1$
    284 
    285         int caretDelta = caretLocation.indexOf("^"); //$NON-NLS-1$
    286         assertTrue(caretLocation, caretDelta != -1);
    287 
    288         // String around caret/range without the range and caret marker characters
    289         String caretContext;
    290         if (caretLocation.contains("[^")) { //$NON-NLS-1$
    291             caretDelta--;
    292             assertTrue(caretLocation, caretLocation.startsWith("[^", caretDelta)); //$NON-NLS-1$
    293             int caretRangeEnd = caretLocation.indexOf(']', caretDelta + 2);
    294             assertTrue(caretLocation, caretRangeEnd != -1);
    295             caretContext = caretLocation.substring(0, caretDelta)
    296                     + caretLocation.substring(caretDelta + 2, caretRangeEnd)
    297                     + caretLocation.substring(caretRangeEnd + 1);
    298         } else {
    299             caretContext = caretLocation.substring(0, caretDelta)
    300                     + caretLocation.substring(caretDelta + 1); // +1: skip "^"
    301         }
    302 
    303         int caretContextIndex = fileContent.indexOf(caretContext);
    304         assertTrue("Caret content " + caretContext + " not found in file",
    305                 caretContextIndex != -1);
    306         return caretContextIndex + caretDelta;
    307     }
    308 
    309     /**
    310      * If the given caret location string contains a selection range, select that range in
    311      * the given viewer
    312      *
    313      * @param viewer the viewer to contain the selection
    314      * @param caretLocation the location string
    315      */
    316     protected int updateCaret(ISourceViewer viewer, String caretLocation) {
    317         assertTrue(caretLocation, caretLocation.contains("^")); //$NON-NLS-1$
    318 
    319         int caretDelta = caretLocation.indexOf("^"); //$NON-NLS-1$
    320         assertTrue(caretLocation, caretDelta != -1);
    321         String text = viewer.getTextWidget().getText();
    322 
    323         int length = 0;
    324 
    325         // String around caret/range without the range and caret marker characters
    326         String caretContext;
    327 
    328         if (caretLocation.contains("[^")) { //$NON-NLS-1$
    329             caretDelta--;
    330             assertTrue(caretLocation, caretLocation.startsWith("[^", caretDelta)); //$NON-NLS-1$
    331 
    332             int caretRangeEnd = caretLocation.indexOf(']', caretDelta + 2);
    333             assertTrue(caretLocation, caretRangeEnd != -1);
    334             length = caretRangeEnd - caretDelta - 2;
    335             assertTrue(length > 0);
    336             caretContext = caretLocation.substring(0, caretDelta)
    337                     + caretLocation.substring(caretDelta + 2, caretRangeEnd)
    338                     + caretLocation.substring(caretRangeEnd + 1);
    339         } else {
    340             caretContext = caretLocation.substring(0, caretDelta)
    341                     + caretLocation.substring(caretDelta + 1); // +1: skip "^"
    342         }
    343 
    344         int caretContextIndex = text.indexOf(caretContext);
    345 
    346         assertTrue("Caret content " + caretContext + " not found in file",
    347                 caretContextIndex != -1);
    348 
    349         int offset = caretContextIndex + caretDelta;
    350         viewer.setSelectedRange(offset, length);
    351 
    352         return offset;
    353     }
    354 
    355     protected String addSelection(String newFileContents, Point selectedRange) {
    356         int selectionBegin = selectedRange.x;
    357         int selectionEnd = selectionBegin + selectedRange.y;
    358         return addSelection(newFileContents, selectionBegin, selectionEnd);
    359     }
    360 
    361     protected String addSelection(String newFileContents, int selectionBegin, int selectionEnd) {
    362         // Insert selection markers -- [ ] for the selection range, ^ for the caret
    363         String newFileWithCaret;
    364         if (selectionBegin < selectionEnd) {
    365             newFileWithCaret = newFileContents.substring(0, selectionBegin) + "[^"
    366                     + newFileContents.substring(selectionBegin, selectionEnd) + "]"
    367                     + newFileContents.substring(selectionEnd);
    368         } else {
    369             // Selected range
    370             newFileWithCaret = newFileContents.substring(0, selectionBegin) + "^"
    371                     + newFileContents.substring(selectionBegin);
    372         }
    373 
    374         return newFileWithCaret;
    375     }
    376 
    377     protected String getCaretContext(String file, int offset) {
    378         int windowSize = 20;
    379         int begin = Math.max(0, offset - windowSize / 2);
    380         int end = Math.min(file.length(), offset + windowSize / 2);
    381 
    382         return "..." + file.substring(begin, offset) + "^" + file.substring(offset, end) + "...";
    383     }
    384 
    385     /**
    386      * Very primitive line differ, intended for files where there are very minor changes
    387      * (such as code completion apply-tests)
    388      */
    389     protected String getDiff(String before, String after) {
    390         // Do line by line analysis
    391         String[] beforeLines = before.split("\n");
    392         String[] afterLines = after.split("\n");
    393 
    394         int firstDelta = 0;
    395         for (; firstDelta < Math.min(beforeLines.length, afterLines.length); firstDelta++) {
    396             if (!beforeLines[firstDelta].equals(afterLines[firstDelta])) {
    397                 break;
    398             }
    399         }
    400 
    401         if (firstDelta == beforeLines.length && firstDelta == afterLines.length) {
    402             return "";
    403         }
    404 
    405         // Counts from the end of both arrays
    406         int lastDelta = 0;
    407         for (; lastDelta < Math.min(beforeLines.length, afterLines.length); lastDelta++) {
    408             if (!beforeLines[beforeLines.length - 1 - lastDelta].equals(
    409                     afterLines[afterLines.length - 1 - lastDelta])) {
    410                 break;
    411             }
    412         }
    413 
    414         boolean showBeforeWindow = firstDelta >= beforeLines.length - lastDelta;
    415         boolean showAfterWindow = firstDelta >= afterLines.length - lastDelta;
    416 
    417         StringBuilder sb = new StringBuilder();
    418         if (showAfterWindow && firstDelta > 0) {
    419             sb.append("  ");
    420             sb.append(afterLines[firstDelta - 1]);
    421             sb.append('\n');
    422         }
    423         for (int i = firstDelta; i < beforeLines.length - lastDelta; i++) {
    424             sb.append("<");
    425             if (beforeLines[i].length() > 0) {
    426                 sb.append(" ");
    427             }
    428             sb.append(beforeLines[i]);
    429             sb.append('\n');
    430         }
    431         if (showAfterWindow && lastDelta < afterLines.length - 1) {
    432             sb.append("  ");
    433             sb.append(afterLines[afterLines.length - (lastDelta -1)]);
    434             sb.append('\n');
    435         }
    436 
    437         sb.append("---\n");
    438 
    439         if (showBeforeWindow && firstDelta > 0) {
    440             sb.append("  ");
    441             sb.append(beforeLines[firstDelta - 1]);
    442             sb.append('\n');
    443         }
    444         for (int i = firstDelta; i < afterLines.length - lastDelta; i++) {
    445             sb.append(">");
    446             if (afterLines[i].length() > 0) {
    447                 sb.append(" ");
    448             }
    449             sb.append(afterLines[i]);
    450             sb.append('\n');
    451         }
    452         if (showBeforeWindow && lastDelta < beforeLines.length - 1) {
    453             sb.append("  ");
    454             sb.append(beforeLines[beforeLines.length - (lastDelta -1)]);
    455             sb.append('\n');
    456         }
    457 
    458         return sb.toString();
    459     }
    460 
    461     protected String removeSessionData(String data) {
    462         if (getProject() != null) {
    463             data = data.replace(getProject().getName(), "PROJECTNAME");
    464         }
    465 
    466         return data;
    467     }
    468 
    469     public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) {
    470         if (hasChildren) {
    471             return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0],
    472                     new AttributeDescriptor[0], new ElementDescriptor[1], false);
    473         } else {
    474             return new ViewElementDescriptor(name, fqn);
    475         }
    476     }
    477 
    478     public static UiViewElementNode createNode(UiViewElementNode parent, String fqn,
    479             boolean hasChildren) {
    480         String name = fqn.substring(fqn.lastIndexOf('.') + 1);
    481         ViewElementDescriptor descriptor = createDesc(name, fqn, hasChildren);
    482         if (parent == null) {
    483             // All node hierarchies should be wrapped inside a document node at the root
    484             parent = new UiViewElementNode(createDesc("doc", "doc", true));
    485         }
    486         return (UiViewElementNode) parent.appendNewUiChild(descriptor);
    487     }
    488 
    489     public static UiViewElementNode createNode(String fqn, boolean hasChildren) {
    490         return createNode(null, fqn, hasChildren);
    491     }
    492 
    493     protected String readTestFile(String relativePath, boolean expectExists) {
    494         String path = "testdata" + File.separator + relativePath; //$NON-NLS-1$
    495         InputStream stream =
    496             AdtProjectTest.class.getResourceAsStream(path);
    497         if (!expectExists && stream == null) {
    498             return null;
    499         }
    500 
    501         assertNotNull(relativePath + " does not exist", stream);
    502 
    503         BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
    504         String xml = AdtPlugin.readFile(reader);
    505         assertNotNull(xml);
    506         assertTrue(xml.length() > 0);
    507 
    508         // Remove any references to the project name such that we are isolated from
    509         // that in golden file.
    510         // Appears in strings.xml etc.
    511         xml = removeSessionData(xml);
    512 
    513         return xml;
    514     }
    515 
    516     protected void assertEqualsGolden(String basename, String actual) {
    517         assertEqualsGolden(basename, actual, basename.substring(basename.lastIndexOf('.') + 1));
    518     }
    519 
    520     protected void assertEqualsGolden(String basename, String actual, String newExtension) {
    521         String testName = getName();
    522         if (testName.startsWith("test")) {
    523             testName = testName.substring(4);
    524             if (Character.isUpperCase(testName.charAt(0))) {
    525                 testName = Character.toLowerCase(testName.charAt(0)) + testName.substring(1);
    526             }
    527         }
    528         String expectedName;
    529         String extension = basename.substring(basename.lastIndexOf('.') + 1);
    530         if (newExtension == null) {
    531             newExtension = extension;
    532         }
    533         expectedName = basename.substring(0, basename.indexOf('.'))
    534                 + "-expected-" + testName + '.' + newExtension;
    535         String expected = readTestFile(expectedName, false);
    536         if (expected == null) {
    537             File expectedPath = new File(
    538                     UPDATE_MISSING_FILES ? getTargetDir() : getTempDir(), expectedName);
    539             AdtPlugin.writeFile(expectedPath, actual);
    540             System.out.println("Expected - written to " + expectedPath + ":\n");
    541             System.out.println(actual);
    542             fail("Did not find golden file (" + expectedName + "): Wrote contents as "
    543                     + expectedPath);
    544         } else {
    545             if (!expected.replaceAll("\r\n", "\n").equals(actual.replaceAll("\r\n", "\n"))) {
    546                 File expectedPath = new File(getTempDir(), expectedName);
    547                 File actualPath = new File(getTempDir(),
    548                         expectedName.replace("expected", "actual"));
    549                 AdtPlugin.writeFile(expectedPath, expected);
    550                 AdtPlugin.writeFile(actualPath, actual);
    551                 // Also update data dir with the current value
    552                 if (UPDATE_DIFFERENT_FILES) {
    553                     AdtPlugin.writeFile( new File(getTargetDir(), expectedName), actual);
    554                 }
    555                 System.out.println("The files differ: diff " + expectedPath + " "
    556                         + actualPath);
    557                 assertEquals("The files differ - see " + expectedPath + " versus " + actualPath,
    558                         expected, actual);
    559             }
    560         }
    561     }
    562 
    563     /** Get the location to write missing golden files to */
    564     protected File getTargetDir() {
    565         // Set $ADT_SDK_SOURCE_PATH to point to your git "sdk" directory; if done, then
    566         // if you run a unit test which refers to a golden file which does not exist, it
    567         // will be created directly into the test data directory and you can rerun the
    568         // test
    569         // and it should pass (after you verify that the golden file contains the correct
    570         // result of course).
    571         String sdk = System.getenv("ADT_SDK_SOURCE_PATH");
    572         if (sdk != null) {
    573             File sdkPath = new File(sdk);
    574             if (sdkPath.exists()) {
    575                 File testData = new File(sdkPath, TEST_DATA_REL_PATH.replace('/',
    576                         File.separatorChar));
    577                 if (testData.exists()) {
    578                     return testData;
    579                 }
    580             }
    581         }
    582         return getTempDir();
    583     }
    584 
    585     protected File getTempDir() {
    586         if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
    587             return new File("/tmp"); //$NON-NLS-1$
    588         }
    589 
    590         if (sTempDir == null) {
    591             // On Windows, we don't want to pollute the temp folder (which is generally
    592             // already incredibly busy). So let's create a temp folder for the results.
    593 
    594             File base = new File(System.getProperty("java.io.tmpdir"));     //$NON-NLS-1$
    595 
    596             Calendar c = Calendar.getInstance();
    597             String name = String.format("adtTests_%1$tF_%1$tT", c).replace(':', '-'); //$NON-NLS-1$
    598             File tmpDir = new File(base, name);
    599             if (!tmpDir.exists() && tmpDir.mkdir()) {
    600                 sTempDir = tmpDir;
    601             } else {
    602                 sTempDir = base;
    603             }
    604         }
    605 
    606         return sTempDir;
    607     }
    608 
    609     /** Special editor context set on the model to be rendered */
    610     protected static class TestLayoutEditorDelegate extends LayoutEditorDelegate {
    611 
    612         public TestLayoutEditorDelegate(
    613                 IFile file,
    614                 IStructuredDocument structuredDocument,
    615                 UiDocumentNode uiRootNode) {
    616             super(new TestAndroidXmlCommonEditor(file, structuredDocument, uiRootNode));
    617         }
    618 
    619         static class TestAndroidXmlCommonEditor extends CommonXmlEditor {
    620 
    621             private final IFile mFile;
    622             private final IStructuredDocument mStructuredDocument;
    623             private UiDocumentNode mUiRootNode;
    624 
    625             TestAndroidXmlCommonEditor(
    626                     IFile file,
    627                     IStructuredDocument structuredDocument,
    628                     UiDocumentNode uiRootNode) {
    629                 mFile = file;
    630                 mStructuredDocument = structuredDocument;
    631                 mUiRootNode = uiRootNode;
    632             }
    633 
    634             @Override
    635             public IFile getInputFile() {
    636                 return mFile;
    637             }
    638 
    639             @Override
    640             public IProject getProject() {
    641                 return mFile.getProject();
    642             }
    643 
    644             @Override
    645             public IStructuredDocument getStructuredDocument() {
    646                 return mStructuredDocument;
    647             }
    648 
    649             @Override
    650             public UiDocumentNode getUiRootNode() {
    651                 return mUiRootNode;
    652             }
    653 
    654             @Override
    655             public void editorDirtyStateChanged() {
    656             }
    657 
    658             @Override
    659             public IStructuredModel getModelForRead() {
    660                 IModelManager mm = StructuredModelManager.getModelManager();
    661                 if (mm != null) {
    662                     try {
    663                         return mm.getModelForRead(mFile);
    664                     } catch (Exception e) {
    665                         fail(e.toString());
    666                     }
    667                 }
    668 
    669                 return null;
    670             }
    671         }
    672     }
    673 
    674     public void testDummy() {
    675         // This class contains shared test functionality for testcase subclasses,
    676         // but without an actual test in the class JUnit complains (even if we make
    677         // it abstract)
    678     }
    679 }
    680