blob: 1e9bf882aee9af50ed3da85a6df7d451517eb1f5 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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.tools.idea.editors.theme.preview;
import com.android.SdkConstants;
import com.android.tools.idea.editors.theme.preview.ThemePreviewBuilder;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import junit.framework.TestCase;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.util.HashSet;
import java.util.List;
import static com.android.tools.idea.editors.theme.preview.ThemePreviewBuilder.BUILDER_ATTR_GROUP;
import static com.android.tools.idea.editors.theme.preview.ThemePreviewBuilder.BUILDER_URI;
import static com.android.tools.idea.editors.theme.preview.ThemePreviewBuilder.THEME_PREVIEW_LAYOUT;
public class ThemePreviewBuilderTest extends TestCase {
// Xpath to search all the components
static final String COMPONENTS_XPATH = "/LinearLayout/" + THEME_PREVIEW_LAYOUT + "/RelativeLayout/LinearLayout/*";
static final String GROUP_LABELS_XPATH = "/LinearLayout/" + THEME_PREVIEW_LAYOUT + "/RelativeLayout/TextView/";
public void testEmptyPreview() throws ParserConfigurationException, XPathExpressionException {
Document document = new ThemePreviewBuilder().build();
XPath xPath = XPathFactory.newInstance().newXPath();
assertEquals("One root layout expected", 1, document.getChildNodes().getLength());
Node rootNode = document.getChildNodes().item(0);
assertEquals(SdkConstants.LINEAR_LAYOUT, rootNode.getNodeName());
assertNotNull(rootNode.getAttributes().getNamedItemNS(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_WIDTH));
assertNotNull(rootNode.getAttributes().getNamedItemNS(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_HEIGHT));
NodeList nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(0, nodeList.getLength());
}
public void testBasicComponentList() throws ParserConfigurationException, XPathExpressionException {
Document document = new ThemePreviewBuilder().addAllComponents(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS).build();
XPath xPath = XPathFactory.newInstance().newXPath();
assertEquals("One root layout expected", 1, document.getChildNodes().getLength());
Node rootNode = document.getChildNodes().item(0);
assertEquals(SdkConstants.LINEAR_LAYOUT, rootNode.getNodeName());
assertNotNull(rootNode.getAttributes().getNamedItemNS(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_WIDTH));
assertNotNull(rootNode.getAttributes().getNamedItemNS(SdkConstants.ANDROID_URI, SdkConstants.ATTR_LAYOUT_HEIGHT));
// Find if any elements doesn't have layout_with or layout_height
NodeList nodeList =
(NodeList)xPath.evaluate("//*[not(@*[local-name() = 'layout_width'])]", document.getDocumentElement(), XPathConstants.NODESET);
assertEquals("All components must have 'layout_with'", 0, nodeList.getLength());
nodeList =
(NodeList)xPath.evaluate("//*[not(@*[local-name() = 'layout_height'])]", document.getDocumentElement(), XPathConstants.NODESET);
assertEquals("All components must have 'layout_height'", 0, nodeList.getLength());
}
public void testComponentList() throws ParserConfigurationException, XPathExpressionException {
Document document = new ThemePreviewBuilder().addAllComponents(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS).build();
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS.size(), nodeList.getLength());
ThemePreviewBuilder customComponentBuilder = new ThemePreviewBuilder()
.addAllComponents(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS)
.addComponent(new ThemePreviewBuilder.ComponentDefinition("Test", ThemePreviewBuilder.ComponentGroup.CUSTOM, "Test").setApiLevel(15));
document = customComponentBuilder.build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS.size() + 1, nodeList.getLength());
// This shouldn't filter our custom component
Predicate<ThemePreviewBuilder.ComponentDefinition> api15Filter = new Predicate<ThemePreviewBuilder.ComponentDefinition>() {
@Override
public boolean apply(ThemePreviewBuilder.ComponentDefinition input) {
return input.apiLevel <= 15;
}
};
document = customComponentBuilder.addComponentFilter(api15Filter).build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
Iterable filteredBaseComponents =
Iterables.filter(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS, new Predicate<ThemePreviewBuilder.ComponentDefinition>() {
@Override
public boolean apply(ThemePreviewBuilder.ComponentDefinition input) {
return input.apiLevel <= 15;
}
});
assertEquals(Iterables.size(filteredBaseComponents) + 1, nodeList.getLength());
// This should filter the custom component
Predicate<ThemePreviewBuilder.ComponentDefinition> api14Filter = new Predicate<ThemePreviewBuilder.ComponentDefinition>() {
@Override
public boolean apply(ThemePreviewBuilder.ComponentDefinition input) {
return input.apiLevel <= 14;
}
};
document = customComponentBuilder.addComponentFilter(api14Filter).build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
filteredBaseComponents =
Iterables.filter(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS, new Predicate<ThemePreviewBuilder.ComponentDefinition>() {
@Override
public boolean apply(ThemePreviewBuilder.ComponentDefinition input) {
return input.apiLevel <= 14;
}
});
assertEquals(Iterables.size(filteredBaseComponents), nodeList.getLength());
}
public void testGroups() throws ParserConfigurationException, XPathExpressionException {
Document document = new ThemePreviewBuilder().addAllComponents(ThemePreviewBuilder.AVAILABLE_BASE_COMPONENTS).build();
XPath xPath = XPathFactory.newInstance().newXPath();
NodeList nodeList =
(NodeList)xPath.evaluate(GROUP_LABELS_XPATH + "@*[local-name() = 'text']", document.getDocumentElement(), XPathConstants.NODESET);
HashSet<String> headerTitles = new HashSet<String>();
for (int i = 0; i < nodeList.getLength(); i++) {
String title = nodeList.item(i).getNodeValue();
if (headerTitles.contains(title)) {
fail(String.format("Header '%s' exists more than once", title));
}
headerTitles.add(title);
}
assertFalse(headerTitles.isEmpty());
assertFalse("'Custom' header shouldn't be present when there are no custom components",
headerTitles.contains(ThemePreviewBuilder.ComponentGroup.CUSTOM.name()));
// Check the group id attribute. This attribute is needed to handle clicks on the preview
// Check all components have a group id.
nodeList =
(NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
assertFalse(node.getAttributes().getNamedItemNS(BUILDER_URI, BUILDER_ATTR_GROUP).getNodeValue().isEmpty());
}
// Add custom component
document = new ThemePreviewBuilder()
.addComponent(new ThemePreviewBuilder.ComponentDefinition("Test", ThemePreviewBuilder.ComponentGroup.CUSTOM, "Test")).build();
nodeList =
(NodeList)xPath.evaluate(GROUP_LABELS_XPATH + "@*[local-name() = 'text']", document.getDocumentElement(), XPathConstants.NODESET);
headerTitles.clear();
for (int i = 0; i < nodeList.getLength(); i++) {
headerTitles.add(nodeList.item(i).getNodeValue());
}
assertFalse(headerTitles.isEmpty());
assertTrue("'Custom' header should be present", headerTitles.contains(ThemePreviewBuilder.ComponentGroup.CUSTOM.name));
nodeList =
(NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
for (int i = 0; i < nodeList.getLength(); i++) {
Node node = nodeList.item(i);
assertFalse(node.getAttributes().getNamedItemNS(BUILDER_URI, BUILDER_ATTR_GROUP).getNodeValue().isEmpty());
}
}
public void testSearchFilter() throws ParserConfigurationException, XPathExpressionException {
List<ThemePreviewBuilder.ComponentDefinition> componentDefinitionList = ImmutableList.of(
new ThemePreviewBuilder.ComponentDefinition("Spinner", ThemePreviewBuilder.ComponentGroup.TEXT, "Spinner"),
new ThemePreviewBuilder.ComponentDefinition("Custom_Component", ThemePreviewBuilder.ComponentGroup.CUSTOM, "CustomComponent")
.addAlias("Spinner")
.addAlias("ABC")
.addAlias("DEF")
);
XPath xPath = XPathFactory.newInstance().newXPath();
ThemePreviewBuilder customComponentBuilder = new ThemePreviewBuilder().addAllComponents(componentDefinitionList);
// Check the search "spinner" returns both the actual spinner control and the custom component with the alias
Document document = customComponentBuilder.addComponentFilter(new ThemePreviewBuilder.SearchFilter("SPINNER")).build();
NodeList nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(2, nodeList.getLength());
// Test matching the name
document = customComponentBuilder.addComponentFilter(new ThemePreviewBuilder.SearchFilter("CustomCOMPONENT")).build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(1, nodeList.getLength());
// Test matching the description
document = customComponentBuilder.addComponentFilter(new ThemePreviewBuilder.SearchFilter("Custom_COMPONENT")).build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(1, nodeList.getLength());
// Test searching for an alias that only matches our custom component
document = customComponentBuilder.addComponentFilter(new ThemePreviewBuilder.SearchFilter("AbC")).build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(1, nodeList.getLength());
// Test partial match
document = customComponentBuilder.addComponentFilter(new ThemePreviewBuilder.SearchFilter("EF")).build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(1, nodeList.getLength());
// Test case sensitive search
document = customComponentBuilder.addComponentFilter(new ThemePreviewBuilder.SearchFilter("AbC", true)).build();
nodeList = (NodeList)xPath.evaluate(COMPONENTS_XPATH, document.getDocumentElement(), XPathConstants.NODESET);
assertEquals(0, nodeList.getLength());
}
}