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