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