blob: b40597d4edd28cef75d6a411f929cceca7092f12 [file] [log] [blame]
/*
* Copyright (C) 2010 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.tradefed.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.easymock.EasyMock;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
/** Unit tests for {@link ConfigurationXmlParser}. */
@RunWith(JUnit4.class)
public class ConfigurationXmlParserTest {
private ConfigurationXmlParser xmlParser;
private IConfigDefLoader mMockLoader;
@Before
public void setUp() throws Exception {
mMockLoader = EasyMock.createMock(IConfigDefLoader.class);
xmlParser = new ConfigurationXmlParser(mMockLoader, null);
}
/**
* Normal case test for {@link ConfigurationXmlParser#parse(ConfigurationDef, String,
* InputStream, Map)}.
*/
@Test
public void testParse() throws ConfigurationException {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <test class=\"junit.framework.TestCase\">\n" +
" <option name=\"opName\" value=\"val\" />\n" +
" </test>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
assertEquals(configName, configDef.getName());
assertEquals("desc", configDef.getDescription());
assertEquals(
"junit.framework.TestCase",
configDef.getObjectClassMap().get("test").get(0).mClassName);
assertEquals("junit.framework.TestCase:1:opName", configDef.getOptionList().get(0).name);
assertEquals("val", configDef.getOptionList().get(0).value);
}
/** Test parsing xml when two Tradefed objects are interleaved. */
@Test
public void testParse_interleaved() {
final String normalConfig =
"<configuration description=\"desc\" >\n"
+ " <test class=\"junit.framework.TestCase\">\n"
+ " <option name=\"opName\" value=\"val\" />\n"
+ " <target_preparer class=\"com.targetprep.class\" />\n"
+ " </test>\n"
+ "</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
fail("Should have thrown an exception.");
} catch (ConfigurationException expected) {
// Expected
assertEquals(
"Failed to parse config xml 'config'. Reason: Declared 'target_preparer'"
+ " object inside junit.framework.TestCase:1 is not valid.",
expected.getMessage());
}
}
/** Test parsing xml with a global option */
@Test
public void testParse_globalOption() throws ConfigurationException {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <option name=\"opName\" value=\"val\" />\n" +
" <test class=\"junit.framework.TestCase\">\n" +
" </test>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
assertEquals(configName, configDef.getName());
assertEquals("desc", configDef.getDescription());
assertEquals(
"junit.framework.TestCase",
configDef.getObjectClassMap().get("test").get(0).mClassName);
// the non-namespaced option value should be used
assertEquals("opName", configDef.getOptionList().get(0).name);
assertEquals("val", configDef.getOptionList().get(0).value);
}
/** Test parsing xml with repeated type/class pairs */
@Test
public void testParse_multiple() throws ConfigurationException {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <test class=\"com.android.tradefed.testtype.HostTest\">\n" +
" <option name=\"class\" value=\"val1\" />\n" +
" </test>\n" +
" <test class=\"com.android.tradefed.testtype.HostTest\">\n" +
" <option name=\"class\" value=\"val2\" />\n" +
" </test>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
assertEquals(configName, configDef.getName());
assertEquals("desc", configDef.getDescription());
assertEquals(
"com.android.tradefed.testtype.HostTest",
configDef.getObjectClassMap().get("test").get(0).mClassName);
assertEquals("com.android.tradefed.testtype.HostTest:1:class", configDef.getOptionList().get(0).name);
assertEquals("val1", configDef.getOptionList().get(0).value);
assertEquals(
"com.android.tradefed.testtype.HostTest",
configDef.getObjectClassMap().get("test").get(1).mClassName);
assertEquals("com.android.tradefed.testtype.HostTest:2:class", configDef.getOptionList().get(1).name);
assertEquals("val2", configDef.getOptionList().get(1).value);
}
/** Test parsing a object tag missing a attribute. */
@Test
public void testParse_objectMissingAttr() {
final String config =
"<object name=\"foo\" />";
try {
xmlParser.parse(new ConfigurationDef("foo"), "foo", getStringAsStream(config), null);
fail("ConfigurationException not thrown");
} catch (ConfigurationException e) {
// expected
}
}
/** Test parsing a option tag missing a attribute. */
@Test
public void testParse_optionMissingAttr() {
final String config =
"<option name=\"foo\" />";
try {
xmlParser.parse(new ConfigurationDef("name"), "name", getStringAsStream(config), null);
fail("ConfigurationException not thrown");
} catch (ConfigurationException e) {
// expected
}
}
/** Test parsing a object tag. */
@Test
public void testParse_object() throws ConfigurationException {
final String config =
"<object type=\"foo\" class=\"junit.framework.TestCase\" />";
ConfigurationDef configDef = new ConfigurationDef("name");
xmlParser.parse(configDef, "name", getStringAsStream(config), null);
assertEquals(
"junit.framework.TestCase",
configDef.getObjectClassMap().get("foo").get(0).mClassName);
}
/** Test parsing a include tag. */
@Test
public void testParse_include() throws ConfigurationException {
String includedName = "includeme";
ConfigurationDef configDef = new ConfigurationDef("foo");
mMockLoader.loadIncludedConfiguration(
EasyMock.eq(configDef),
EasyMock.eq("foo"),
EasyMock.eq(includedName),
EasyMock.anyObject(),
EasyMock.anyObject(),
EasyMock.anyObject());
EasyMock.replay(mMockLoader);
final String config = "<include name=\"includeme\" />";
xmlParser.parse(configDef, "foo", getStringAsStream(config), null);
}
/** Test parsing a include tag where named config does not exist */
@Test
public void testParse_includeMissing() throws ConfigurationException {
String includedName = "non-existent";
ConfigurationDef parent = new ConfigurationDef("name");
ConfigurationException exception = new ConfigurationException("I don't exist");
mMockLoader.loadIncludedConfiguration(
parent,
"name",
includedName,
null,
Collections.<String, String>emptyMap(),
new HashSet<>());
EasyMock.expectLastCall().andThrow(exception);
EasyMock.replay(mMockLoader);
final String config = String.format("<include name=\"%s\" />", includedName);
try {
xmlParser.parse(parent, "name", getStringAsStream(config), null);
fail("ConfigurationException not thrown");
} catch (ConfigurationException e) {
// expected
}
}
/** Test parsing a tag whose name is not recognized. */
@Test
public void testParse_badTag() {
final String config = "<blah name=\"foo\" />";
try {
xmlParser.parse(new ConfigurationDef("name"), "name", getStringAsStream(config), null);
fail("ConfigurationException not thrown");
} catch (ConfigurationException e) {
// expected
}
}
/** Test parsing invalid xml. */
@Test
public void testParse_xml() {
final String config = "blah";
try {
xmlParser.parse(new ConfigurationDef("name"), "name", getStringAsStream(config), null);
fail("ConfigurationException not thrown");
} catch (ConfigurationException e) {
// expected
}
}
private InputStream getStringAsStream(String input) {
return new ByteArrayInputStream(input.getBytes());
}
/**
* Normal case test for {@link ConfigurationXmlParser#parse(ConfigurationDef, String,
* InputStream, Map)}. when presented a device tag.
*/
@Test
public void testParse_deviceTag() throws ConfigurationException {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <device name=\"device1\">\n" +
" <option name=\"opName\" value=\"val\" />\n" +
" </device>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
assertEquals(configName, configDef.getName());
assertEquals("desc", configDef.getDescription());
// Option is preprended with the device name.
assertEquals("{device1}opName", configDef.getOptionList().get(0).name);
assertEquals("val", configDef.getOptionList().get(0).value);
}
/**
* Test case for {@link ConfigurationXmlParser#parse(ConfigurationDef, String, InputStream,
* Map)}. when presented a device tag with no name.
*/
@Test
public void testParse_deviceTagNoName() {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <device>\n" +
" <option name=\"opName\" value=\"val\" />\n" +
" </device>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
String expectedException = "device tag requires a name value";
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
fail("An exception should have been thrown.");
} catch (ConfigurationException expected) {
assertEquals(expectedException, expected.getMessage());
}
}
/**
* Test case for {@link ConfigurationXmlParser#parse(ConfigurationDef, String, InputStream,
* Map)}. when presented a device tag with a used name, should merge them.
*/
@Test
public void testParse_deviceTagSameName() throws Exception {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <device name=\"device1\">\n" +
" <option name=\"opName\" value=\"val\" />\n" +
" </device>\n" +
" <device name=\"device2\">\n" +
" <option name=\"opName3\" value=\"val3\" />\n" +
" </device>\n" +
" <device name=\"device1\">\n" +
" <option name=\"opName2\" value=\"val2\" />\n" +
" </device>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
assertTrue(configDef.getObjectClassMap().get(Configuration.DEVICE_NAME).size() == 2);
assertTrue("{device1}opName".equals(configDef.getOptionList().get(0).name));
assertEquals("{device2}opName3", configDef.getOptionList().get(1).name);
assertTrue("{device1}opName2".equals(configDef.getOptionList().get(2).name));
}
/**
* Test case for {@link ConfigurationXmlParser#parse(ConfigurationDef, String, InputStream,
* Map)}. when presented an object tag outside of the device tag but should be inside.
*/
@Test
public void testParse_deviceTagAndObjectOutside() {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <device name=\"device1\">\n" +
" <option name=\"opName\" value=\"val\" />\n" +
" </device>\n" +
" <target_preparer class=\"com.targetprep.class\">\n" +
" <option name=\"opName2\" value=\"val2\" />\n" +
" </target_preparer>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
String expectedException =
"You seem to want a multi-devices configuration but you have "
+ "[target_preparer] tags outside the <device> tags";
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
fail("An exception should have been thrown.");
} catch(ConfigurationException expected) {
assertEquals(expectedException, expected.getMessage());
}
}
/**
* Test for {@link ConfigurationXmlParser#parse(ConfigurationDef, String, InputStream, Map)}.
* with a test tag inside a device where it should not be.
*/
@Test
public void testParse_withDeviceTag() {
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <device name=\"device1\">\n" +
" <option name=\"deviceOp\" value=\"val2\" />\n" +
" <test class=\"junit.framework.TestCase\">\n" +
" <option name=\"opName\" value=\"val\" />\n" +
" </test>\n" +
" </device>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
} catch(ConfigurationException expected) {
return;
}
fail("An exception should have been thrown.");
}
/**
* Test for {@link ConfigurationXmlParser#parse(ConfigurationDef, String, InputStream, Map)}.
* with an invalid name
*/
@Test
public void testParse_withDeviceInvalidName() {
String expectedException = "device name cannot contain reserved character: ':'";
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <device name=\"device:1\">\n" +
" </device>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
fail("An exception should have been thrown.");
} catch(ConfigurationException expected) {
assertEquals(expectedException, expected.getMessage());
}
}
/**
* Test for {@link ConfigurationXmlParser#parse(ConfigurationDef, String, InputStream, Map)}.
* with a reserved name.
*/
@Test
public void testParse_withDeviceReservedName() {
String expectedException = "device name cannot be reserved name: '" +
ConfigurationDef.DEFAULT_DEVICE_NAME + "'";
final String normalConfig =
"<configuration description=\"desc\" >\n" +
" <device name=\"" + ConfigurationDef.DEFAULT_DEVICE_NAME + "\">\n" +
" </device>\n" +
"</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
fail("An exception should have been thrown.");
} catch(ConfigurationException expected) {
assertEquals(expectedException, expected.getMessage());
}
}
/**
* Test for {@link ConfigurationXmlParser#parse(ConfigurationDef, String, InputStream, Map)}.
* with a include that has extra attributes.
*/
@Test
public void testParse_includeWithExtraAttributes() {
String expectedException =
"Failed to parse config xml 'config'. Reason: <include> tag only expect a 'name' "
+ "attribute.";
final String normalConfig =
"<configuration description=\"desc\" >\n"
+ " <include name=\"default\" other=\"test\">\n"
+ "</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
fail("An exception should have been thrown.");
} catch (ConfigurationException expected) {
assertEquals(expectedException, expected.getMessage());
}
}
/** Prevent template-include with the same name from appearing. */
@Test
public void testParse_repeatedTemplateName() {
String expectedException =
"Failed to parse config xml 'config'. Reason: Template named 'preparers' "
+ "appeared more than once.";
final String normalConfig =
"<configuration description=\"desc\" >\n"
+ " <template-include name=\"preparers\" default=\"empty\"/>\n"
+ " <template-include name=\"preparers\" default=\"empty\"/>\n"
+ "</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
try {
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
fail("An exception should have been thrown.");
} catch (ConfigurationException expected) {
assertEquals(expectedException, expected.getMessage());
}
}
/**
* Test that if an object is left at the root of the config but with one real and one fake
* device, we do not reject the xml. Object will be associated later to the real device.
*/
@Test
public void testParse_multiDevice_fakeMulti() throws Exception {
final String normalConfig =
"<configuration description=\"desc\" >\n"
+ " <device name=\"device1\">\n"
+ " <option name=\"opName\" value=\"val\" />\n"
+ " </device>\n"
+ " <device name=\"device2\" isFake=\"true\">\n"
+ " <option name=\"opName3\" value=\"val3\" />\n"
+ " </device>\n"
+ " <target_preparer class=\"com.targetprep.class\">\n"
+ " <option name=\"opName2\" value=\"val2\" />\n"
+ " </target_preparer>\n"
+ "</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
assertEquals(2, configDef.getObjectClassMap().get(Configuration.DEVICE_NAME).size());
assertTrue("{device1}opName".equals(configDef.getOptionList().get(0).name));
assertEquals("{device2}opName3", configDef.getOptionList().get(1).name);
}
/**
* Test that if we have only fake devices, an object cannot be left at the root because we only
* consider it properly formatted if at least one real device exists.
*/
@Test
public void testParse_multiDevice_fakeMulti_noReal() throws Exception {
final String normalConfig =
"<configuration description=\"desc\" >\n"
+ " <device name=\"device1\" isFake=\"true\">\n"
+ " <option name=\"opName\" value=\"val\" />\n"
+ " </device>\n"
+ " <device name=\"device2\" isFake=\"true\">\n"
+ " <option name=\"opName3\" value=\"val3\" />\n"
+ " </device>\n"
+ " <target_preparer class=\"com.targetprep.class\">\n"
+ " <option name=\"opName2\" value=\"val2\" />\n"
+ " </target_preparer>\n"
+ "</configuration>";
final String configName = "config";
ConfigurationDef configDef = new ConfigurationDef(configName);
xmlParser.parse(configDef, configName, getStringAsStream(normalConfig), null);
// Two fakes devices, the root device will be added during creation of configuration
assertEquals(2, configDef.getObjectClassMap().get(Configuration.DEVICE_NAME).size());
}
}