blob: 9565183bc35887d81d4dbd3012301859158e1c32 [file] [log] [blame]
/*
* 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.ANDROID_WIDGET_PREFIX;
import static com.android.SdkConstants.DOT_XML;
import com.android.ide.common.rendering.api.ViewInfo;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.preferences.AdtPrefs;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.TextEdit;
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.IndexedRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.Element;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@SuppressWarnings("restriction")
public class RefactoringTest extends AdtProjectTest {
protected boolean autoFormat() {
return true;
}
@Override
protected void setUp() throws Exception {
// Ensure that the defaults are initialized so for example formatting options are
// initialized properly
IPreferenceStore store = AdtPlugin.getDefault().getPreferenceStore();
AdtPrefs.init(store);
AdtPrefs prefs = AdtPrefs.getPrefs();
prefs.initializeStoreWithDefaults(store);
store.setValue(AdtPrefs.PREFS_FORMAT_GUI_XML, autoFormat());
prefs.loadValues(null);
super.setUp();
}
protected static Element findElementById(Element root, String id) {
if (id.equals(VisualRefactoring.getId(root))) {
return root;
}
for (Element child : DomUtilities.getChildren(root)) {
Element result = findElementById(child, id);
if (result != null) {
return result;
}
}
return null;
}
protected static List<Element> getElements(Element root, String... ids) {
List<Element> selectedElements = new ArrayList<Element>();
for (String id : ids) {
Element element = findElementById(root, id);
assertNotNull(element);
selectedElements.add(element);
}
return selectedElements;
}
protected void checkEdits(String basename, List<Change> changes) throws BadLocationException,
IOException {
IDocument document = new Document();
String xml = readTestFile(basename, false);
if (xml == null) { // New file
xml = ""; //$NON-NLS-1$
}
document.set(xml);
for (Change change : changes) {
if (change instanceof TextFileChange) {
TextFileChange tf = (TextFileChange) change;
TextEdit edit = tf.getEdit();
IFile file = tf.getFile();
String contents = AdtPlugin.readFile(file);
assertEquals(contents, xml);
if (edit instanceof MultiTextEdit) {
MultiTextEdit edits = (MultiTextEdit) edit;
edits.apply(document);
} else {
edit.apply(document);
}
} else {
System.out.println("Ignoring non-textfilechange in refactoring result");
}
}
String actual = document.get();
// Ensure that the document is still valid to make sure the edits don't
// mangle it:
org.w3c.dom.Document doc = DomUtilities.parseDocument(actual, true);
assertNotNull(actual, doc);
assertEqualsGolden(basename, actual);
}
protected void checkEdits(List<Change> changes,
Map<IPath, String> fileToGoldenName) throws BadLocationException, IOException {
checkEdits(changes, fileToGoldenName, false);
}
protected void checkEdits(List<Change> changes,
Map<IPath, String> fileToGoldenName, boolean createDiffs)
throws BadLocationException, IOException {
for (Change change : changes) {
if (change instanceof TextFileChange) {
TextFileChange tf = (TextFileChange) change;
IFile file = tf.getFile();
assertNotNull(file);
IPath path = file.getProjectRelativePath();
String goldenName = fileToGoldenName.get(path);
assertNotNull("Not found: " + path.toString(), goldenName);
String xml = readTestFile(goldenName, false);
if (xml == null) { // New file
xml = ""; //$NON-NLS-1$
}
IDocument document = new Document();
document.set(xml);
String before = document.get();
TextEdit edit = tf.getEdit();
if (edit instanceof MultiTextEdit) {
MultiTextEdit edits = (MultiTextEdit) edit;
edits.apply(document);
} else {
edit.apply(document);
}
String actual = document.get();
if (createDiffs) {
// Use a diff as the golden file instead of the after
actual = getDiff(before, actual);
if (goldenName.endsWith(DOT_XML)) {
goldenName = goldenName.substring(0,
goldenName.length() - DOT_XML.length())
+ ".diff";
}
}
assertEqualsGolden(goldenName, actual);
} else {
System.out.println("Ignoring non-textfilechange in refactoring result");
assertNull(change.getAffectedObjects());
}
}
}
protected UiViewElementNode createModel(UiViewElementNode parent, Element element) {
List<Element> children = DomUtilities.getChildren(element);
String fqcn = ANDROID_WIDGET_PREFIX + element.getTagName();
boolean hasChildren = children.size() > 0;
UiViewElementNode node = createNode(parent, fqcn, hasChildren);
node.setXmlNode(element);
for (Element child : children) {
createModel(node, child);
}
return node;
}
/**
* Builds up a ViewInfo hierarchy for the given model. This is done by
* reading .info dump files which record the exact pixel sizes of each
* ViewInfo object. These files are assumed to match up exactly with the
* model objects. This is done rather than rendering an actual layout
* hierarchy to insulate the test from pixel difference (in say font size)
* among platforms, as well as tying the test to particulars about relative
* sizes of things which may change with theme adjustments etc.
* <p>
* Each file can be generated by the dump method in the ViewHierarchy.
*/
protected ViewInfo createInfos(UiElementNode model, String relativePath) throws IOException {
String basename = relativePath.substring(0, relativePath.lastIndexOf('.') + 1);
String relative = basename + "info"; //$NON-NLS-1$
String info = readTestFile(relative, true);
// Parse the info file and build up a model from it
// Each line contains a new info.
// If indented it is a child of the parent.
String[] lines = info.split("\n"); //$NON-NLS-1$
// Iteration order for the info file should match exactly the UI model so
// we can just advance the line index sequentially as we traverse
return create(model, Arrays.asList(lines).iterator());
}
protected ViewInfo create(UiElementNode node, Iterator<String> lineIterator) {
// android.widget.LinearLayout [0,36,240,320]
Pattern pattern = Pattern.compile("(\\s*)(\\S+) \\[(\\d+),(\\d+),(\\d+),(\\d+)\\].*");
assertTrue(lineIterator.hasNext());
String description = lineIterator.next();
Matcher matcher = pattern.matcher(description);
assertTrue(matcher.matches());
//String indent = matcher.group(1);
//String fqcn = matcher.group(2);
String left = matcher.group(3);
String top = matcher.group(4);
String right = matcher.group(5);
String bottom = matcher.group(6);
ViewInfo view = new ViewInfo(node.getXmlNode().getLocalName(), node,
Integer.parseInt(left), Integer.parseInt(top),
Integer.parseInt(right), Integer.parseInt(bottom));
List<UiElementNode> childNodes = node.getUiChildren();
if (childNodes.size() > 0) {
List<ViewInfo> children = new ArrayList<ViewInfo>();
for (UiElementNode child : childNodes) {
children.add(create(child, lineIterator));
}
view.setChildren(children);
}
return view;
}
protected TestContext setupTestContext(IFile file, String relativePath) throws Exception {
IStructuredModel structuredModel = null;
org.w3c.dom.Document domDocument = null;
IStructuredDocument structuredDocument = null;
Element element = null;
try {
IModelManager modelManager = StructuredModelManager.getModelManager();
structuredModel = modelManager.getModelForRead(file);
if (structuredModel instanceof IDOMModel) {
IDOMModel domModel = (IDOMModel) structuredModel;
domDocument = domModel.getDocument();
element = domDocument.getDocumentElement();
structuredDocument = structuredModel.getStructuredDocument();
}
} finally {
if (structuredModel != null) {
structuredModel.releaseFromRead();
}
}
assertNotNull(structuredModel);
assertNotNull(domDocument);
assertNotNull(element);
assertNotNull(structuredDocument);
assertTrue(element instanceof IndexedRegion);
UiViewElementNode model = createModel(null, element);
ViewInfo info = createInfos(model, relativePath);
CanvasViewInfo rootView = CanvasViewInfo.create(info, true /* layoutlib5 */).getFirst();
TestLayoutEditorDelegate layoutEditor =
new TestLayoutEditorDelegate(file, structuredDocument, null);
TestContext testInfo = createTestContext();
testInfo.mFile = file;
testInfo.mStructuredModel = structuredModel;
testInfo.mStructuredDocument = structuredDocument;
testInfo.mElement = element;
testInfo.mDomDocument = domDocument;
testInfo.mUiModel = model;
testInfo.mViewInfo = info;
testInfo.mRootView = rootView;
testInfo.mLayoutEditorDelegate = layoutEditor;
return testInfo;
}
protected TestContext createTestContext() {
return new TestContext();
}
protected static class TestContext {
protected IFile mFile;
protected IStructuredModel mStructuredModel;
protected IStructuredDocument mStructuredDocument;
protected org.w3c.dom.Document mDomDocument;
protected Element mElement;
protected UiViewElementNode mUiModel;
protected ViewInfo mViewInfo;
protected CanvasViewInfo mRootView;
protected TestLayoutEditorDelegate mLayoutEditorDelegate;
}
@Override
public void testDummy() {
// To avoid JUnit warning that this class contains no tests, even though
// this is an abstract class and JUnit shouldn't try
}
}