You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by js...@apache.org on 2014/11/10 22:32:05 UTC

[06/11] ambari git commit: AMBARI-7175. Add explicit stack service inheritance

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fc7adec/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintEntityTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintEntityTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintEntityTest.java
index d8c0e29..b965554 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintEntityTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/entities/BlueprintEntityTest.java
@@ -22,12 +22,15 @@ import com.google.gson.Gson;
 
 import org.apache.ambari.server.api.services.AmbariMetaInfo;
 import org.apache.ambari.server.state.PropertyInfo;
+import org.apache.ambari.server.state.ServiceInfo;
 import org.junit.Test;
 
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -83,8 +86,11 @@ public class BlueprintEntityTest {
   @Test
   public void testValidateConfigurations_clusterConfig() throws Exception {
     AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
+    ServiceInfo service = new ServiceInfo();
+    service.setName("service1");
+
+    List<PropertyInfo> serviceProperties = new ArrayList<PropertyInfo>();
 
-    Map<String, PropertyInfo> requiredProps = new HashMap<String, PropertyInfo>();
     PropertyInfo prop = new PropertyInfo();
     prop.setFilename("core-site.xml");
     prop.setName("super.secret.password");
@@ -93,7 +99,9 @@ public class BlueprintEntityTest {
     propertyTypes.add(PropertyInfo.PropertyType.PASSWORD);
     prop.setPropertyTypes(propertyTypes);
     prop.setValue(null);
-    requiredProps.put("super.secret.password", prop);
+    serviceProperties.add(prop);
+    service.getProperties().addAll(serviceProperties);
+    service.getProperties().addAll(serviceProperties);
 
     BlueprintEntity entity = new BlueprintEntity();
     entity.setStackName("stackName");
@@ -130,7 +138,7 @@ public class BlueprintEntityTest {
     entity.setHostGroups(hostGroupEntities);
 
     expect(metaInfo.getComponentToService("stackName", "version", "component1")).andReturn("service1");
-    expect(metaInfo.getRequiredProperties("stackName", "version", "service1")).andReturn(requiredProps);
+    expect(metaInfo.getService("stackName", "version", "service1")).andReturn(service);
 
     replay(metaInfo);
 
@@ -145,17 +153,20 @@ public class BlueprintEntityTest {
   @Test
   public void testValidateConfigurations_hostGroupConfig() throws Exception {
     AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
-
-    Map<String, PropertyInfo> requiredProps = new HashMap<String, PropertyInfo>();
-    PropertyInfo prop = new PropertyInfo();
-    prop.setFilename("core-site.xml");
-    prop.setName("super.secret.password");
-    prop.setRequireInput(true);
+    ServiceInfo service = new ServiceInfo();
+    service.setName("service1");
+
+    List<PropertyInfo> serviceProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo prop1 = new PropertyInfo();
+    prop1.setFilename("core-site.xml");
+    prop1.setName("super.secret.password");
+    prop1.setRequireInput(true);
     Set<PropertyInfo.PropertyType> propertyTypes = new HashSet<PropertyInfo.PropertyType>();
     propertyTypes.add(PropertyInfo.PropertyType.PASSWORD);
-    prop.setPropertyTypes(propertyTypes);
-    prop.setValue(null);
-    requiredProps.put("super.secret.password", prop);
+    prop1.setPropertyTypes(propertyTypes);
+    prop1.setValue(null);
+    serviceProperties.add(prop1);
+    service.getProperties().addAll(serviceProperties);
 
     BlueprintEntity entity = new BlueprintEntity();
     entity.setStackName("stackName");
@@ -193,7 +204,7 @@ public class BlueprintEntityTest {
     entity.setHostGroups(hostGroupEntities);
 
     expect(metaInfo.getComponentToService("stackName", "version", "component1")).andReturn("service1");
-    expect(metaInfo.getRequiredProperties("stackName", "version", "service1")).andReturn(requiredProps);
+    expect(metaInfo.getService("stackName", "version", "service1")).andReturn(service);
 
     replay(metaInfo);
 
@@ -208,16 +219,20 @@ public class BlueprintEntityTest {
   @Test
   public void testValidateConfigurations_negative() throws Exception {
     AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
+    ServiceInfo service = new ServiceInfo();
+    service.setName("service1");
 
-    Map<String, PropertyInfo> requiredProps = new HashMap<String, PropertyInfo>();
-    PropertyInfo prop = new PropertyInfo();
-    prop.setFilename("core-site.xml");
-    prop.setName("super.secret.password");
-    prop.setRequireInput(true);
+    List<PropertyInfo> serviceProperties = new ArrayList<PropertyInfo>();
+
+    PropertyInfo prop1 = new PropertyInfo();
+    prop1.setFilename("core-site.xml");
+    prop1.setName("super.secret.password");
+    prop1.setRequireInput(true);
     Set<PropertyInfo.PropertyType> propertyTypes = new HashSet<PropertyInfo.PropertyType>();
     propertyTypes.add(PropertyInfo.PropertyType.PASSWORD);
-    prop.setPropertyTypes(propertyTypes);
-    prop.setValue(null);
+    prop1.setPropertyTypes(propertyTypes);
+    prop1.setValue(null);
+    serviceProperties.add(prop1);
 
     PropertyInfo prop2 = new PropertyInfo();
     prop2.setFilename("global.xml");
@@ -227,9 +242,9 @@ public class BlueprintEntityTest {
     propertyTypes2.add(PropertyInfo.PropertyType.PASSWORD);
     prop2.setPropertyTypes(propertyTypes2);
     prop2.setValue(" ");
+    serviceProperties.add(prop2);
 
-    requiredProps.put("super.secret.password", prop);
-    requiredProps.put("another.super.secret.password", prop2);
+    service.getProperties().addAll(serviceProperties);
 
     BlueprintEntity entity = new BlueprintEntity();
     entity.setStackName("stackName");
@@ -266,7 +281,7 @@ public class BlueprintEntityTest {
     entity.setHostGroups(hostGroupEntities);
 
     expect(metaInfo.getComponentToService("stackName", "version", "component1")).andReturn("service1");
-    expect(metaInfo.getRequiredProperties("stackName", "version", "service1")).andReturn(requiredProps);
+    expect(metaInfo.getService("stackName", "version", "service1")).andReturn(service);
 
     replay(metaInfo);
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fc7adec/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java b/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java
new file mode 100644
index 0000000..8181cbc
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/stack/ComponentModuleTest.java
@@ -0,0 +1,409 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.stack;
+
+import org.apache.ambari.server.state.AutoDeployInfo;
+import org.apache.ambari.server.state.ClientConfigFileDefinition;
+import org.apache.ambari.server.state.CommandScriptDefinition;
+import org.apache.ambari.server.state.ComponentInfo;
+import org.apache.ambari.server.state.CustomCommandDefinition;
+import org.apache.ambari.server.state.DependencyInfo;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ComponentModule unit test case.
+ */
+public class ComponentModuleTest {
+
+  @Test
+  public void testResolve_CommandScript() {
+    CommandScriptDefinition commandScript = new CommandScriptDefinition();
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setCommandScript(commandScript);
+    assertSame(commandScript, resolveComponent(info, parentInfo).getModuleInfo().getCommandScript());
+
+    // child has value set, parent value is null
+    info.setCommandScript(commandScript);
+    parentInfo.setCommandScript(null);
+    assertSame(commandScript, resolveComponent(info, parentInfo).getModuleInfo().getCommandScript());
+
+    // value set in both parent and child; child overwrites
+    CommandScriptDefinition commandScript2 = createNiceMock(CommandScriptDefinition.class);
+    info.setCommandScript(commandScript);
+    parentInfo.setCommandScript(commandScript2);
+    assertSame(commandScript, resolveComponent(info, parentInfo).getModuleInfo().getCommandScript());
+  }
+
+  @Test
+  public void testResolve_DisplayName() {
+    String displayName = "foo";
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setDisplayName(displayName);
+    assertEquals(displayName, resolveComponent(info, parentInfo).getModuleInfo().getDisplayName());
+
+    // child has value set, parent value is null
+    info.setDisplayName(displayName);
+    parentInfo.setDisplayName(null);
+    assertEquals(displayName, resolveComponent(info, parentInfo).getModuleInfo().getDisplayName());
+
+    // value set in both parent and child; child overwrites
+    String displayName2 = "foo2";
+    info.setDisplayName(displayName2);
+    parentInfo.setDisplayName(displayName);
+    assertEquals(displayName2, resolveComponent(info, parentInfo).getModuleInfo().getDisplayName());
+  }
+
+  @Test
+  public void testResolve_ClientConfigFiles() {
+    List<ClientConfigFileDefinition> clientConfigs = new ArrayList<ClientConfigFileDefinition>();
+    ClientConfigFileDefinition clientConfig1 = new ClientConfigFileDefinition();
+    clientConfig1.setType("type1");
+    clientConfig1.setDictionaryName("dictName1");
+    clientConfig1.setFileName("filename1");
+    ClientConfigFileDefinition clientConfig2 = new ClientConfigFileDefinition();
+    clientConfig1.setType("type1");
+    clientConfig1.setDictionaryName("dictName1");
+    clientConfig1.setFileName("filename1");
+    clientConfigs.add(clientConfig1);
+    clientConfigs.add(clientConfig2);
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setClientConfigFiles(clientConfigs);
+    assertEquals(clientConfigs, resolveComponent(info, parentInfo).getModuleInfo().getClientConfigFiles());
+
+    // child has value set, parent value is null
+    info.setClientConfigFiles(clientConfigs);
+    parentInfo.setClientConfigFiles(null);
+    assertEquals(clientConfigs, resolveComponent(info, parentInfo).getModuleInfo().getClientConfigFiles());
+
+    // value set in both parent and child; child overwrites with no merge
+    List<ClientConfigFileDefinition> clientConfigs2 = new ArrayList<ClientConfigFileDefinition>();
+    ClientConfigFileDefinition clientConfig3 = new ClientConfigFileDefinition();
+    clientConfig3.setType("type1");
+    clientConfig3.setDictionaryName("dictName1");
+    clientConfig3.setFileName("DIFFERENT filename");
+    clientConfigs2.add(clientConfig3);
+
+    info.setClientConfigFiles(clientConfigs2);
+    parentInfo.setClientConfigFiles(clientConfigs);
+    assertEquals(clientConfigs2, resolveComponent(info, parentInfo).getModuleInfo().getClientConfigFiles());
+  }
+
+  @Test
+  public void testResolve_Category() {
+    String category = "foo";
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setCategory(category);
+    assertEquals(category, resolveComponent(info, parentInfo).getModuleInfo().getCategory());
+
+    // child has value set, parent value is null
+    info.setCategory(category);
+    parentInfo.setCategory(null);
+    assertEquals(category, resolveComponent(info, parentInfo).getModuleInfo().getCategory());
+
+    // value set in both parent and child; child overwrites
+    String category2 = "foo2";
+    info.setCategory(category2);
+    parentInfo.setCategory(category);
+    assertEquals(category2, resolveComponent(info, parentInfo).getModuleInfo().getCategory());
+  }
+
+  @Test
+  public void testResolve_Cardinality() {
+    String cardinality = "foo";
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setCardinality(cardinality);
+    assertEquals(cardinality, resolveComponent(info, parentInfo).getModuleInfo().getCardinality());
+
+    // child has value set, parent value is null
+    info.setCardinality(cardinality);
+    parentInfo.setCardinality(null);
+    assertEquals(cardinality, resolveComponent(info, parentInfo).getModuleInfo().getCardinality());
+
+    // value set in both parent and child; child overwrites
+    String cardinality2 = "foo2";
+    info.setCardinality(cardinality2);
+    parentInfo.setCardinality(cardinality);
+    assertEquals(cardinality2, resolveComponent(info, parentInfo).getModuleInfo().getCardinality());
+  }
+
+  @Test
+  public void testResolve_AutoDeploy() {
+    AutoDeployInfo autoDeployInfo = new AutoDeployInfo();
+    autoDeployInfo.setEnabled(true);
+    autoDeployInfo.setCoLocate("foo/bar");
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setAutoDeploy(autoDeployInfo);
+    assertEquals(autoDeployInfo, resolveComponent(info, parentInfo).getModuleInfo().getAutoDeploy());
+
+    // child has value set, parent value is null
+    info.setAutoDeploy(autoDeployInfo);
+    parentInfo.setAutoDeploy(null);
+    assertEquals(autoDeployInfo, resolveComponent(info, parentInfo).getModuleInfo().getAutoDeploy());
+
+    // value set in both parent and child; child overwrites
+    AutoDeployInfo autoDeployInfo2 = new AutoDeployInfo();
+    info.setAutoDeploy(autoDeployInfo);
+    parentInfo.setAutoDeploy(autoDeployInfo2);
+    assertEquals(autoDeployInfo, resolveComponent(info, parentInfo).getModuleInfo().getAutoDeploy());
+  }
+
+
+  @Test
+  public void testResolve_Dependencies() {
+    List<DependencyInfo> dependencies = new ArrayList<DependencyInfo>();
+    DependencyInfo dependency1 = new DependencyInfo();
+    dependency1.setName("service/one");
+    DependencyInfo dependency2 = new DependencyInfo();
+    dependency2.setName("service/two");
+    dependencies.add(dependency1);
+    dependencies.add(dependency2);
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setDependencies(dependencies);
+    assertEquals(dependencies, resolveComponent(info, parentInfo).getModuleInfo().getDependencies());
+
+    // child has value set, parent value is null
+    info.setDependencies(dependencies);
+    parentInfo.setDependencies(null);
+    assertEquals(dependencies, resolveComponent(info, parentInfo).getModuleInfo().getDependencies());
+
+    // value set in both parent and child; merge parent and child
+    //todo: currently there is no way to remove an inherited dependency
+    List<DependencyInfo> dependencies2 = new ArrayList<DependencyInfo>();
+    DependencyInfo dependency3 = new DependencyInfo();
+    dependency3.setName("service/two");
+    DependencyInfo dependency4 = new DependencyInfo();
+    dependency4.setName("service/four");
+    dependencies2.add(dependency3);
+    dependencies2.add(dependency4);
+
+    info.setDependencies(dependencies2);
+    parentInfo.setDependencies(dependencies);
+
+    List<DependencyInfo> resolvedDependencies = resolveComponent(info, parentInfo).getModuleInfo().getDependencies();
+    assertEquals(3, resolvedDependencies.size());
+    assertTrue(resolvedDependencies.contains(dependency1));
+    assertTrue(resolvedDependencies.contains(dependency3));
+    assertTrue(resolvedDependencies.contains(dependency4));
+  }
+
+  @Test
+  public void testResolve_CustomCommands() throws Exception {
+    List<CustomCommandDefinition> commands = new ArrayList<CustomCommandDefinition>();
+    CustomCommandDefinition command1 = new CustomCommandDefinition();
+    setPrivateField(command1, "name", "one");
+    CustomCommandDefinition command2 = new CustomCommandDefinition();
+    setPrivateField(command2, "name", "two");
+    commands.add(command1);
+    commands.add(command2);
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setCustomCommands(commands);
+    assertEquals(commands, resolveComponent(info, parentInfo).getModuleInfo().getCustomCommands());
+
+    // child has value set, parent value is null
+    info.setCustomCommands(commands);
+    parentInfo.setCustomCommands(null);
+    assertEquals(commands, resolveComponent(info, parentInfo).getModuleInfo().getCustomCommands());
+
+    // value set in both parent and child; merge parent and child
+    //todo: currently there is no way to remove an inherited command
+    List<CustomCommandDefinition> commands2 = new ArrayList<CustomCommandDefinition>();
+    CustomCommandDefinition command3 = new CustomCommandDefinition();
+    // override command 2
+    setPrivateField(command3, "name", "two");
+    CustomCommandDefinition command4 = new CustomCommandDefinition();
+    setPrivateField(command4, "name", "four");
+    commands2.add(command3);
+    commands2.add(command4);
+
+    info.setCustomCommands(commands2);
+    parentInfo.setCustomCommands(commands);
+
+    List<CustomCommandDefinition> resolvedCommands = resolveComponent(info, parentInfo).getModuleInfo().getCustomCommands();
+    assertEquals(3, resolvedCommands.size());
+    assertTrue(resolvedCommands.contains(command1));
+    assertTrue(resolvedCommands.contains(command3));
+    assertTrue(resolvedCommands.contains(command4));
+  }
+
+  @Test
+  // merging of config dependencies is different than other non-module merges in that the collections aren't
+  // merged if any config dependency is specified in the child.  So, the merged result is either the child
+  // dependencies or if null, the parent dependencies.
+  public void testResolve_ConfigDependencies() {
+    List<String> dependencies = new ArrayList<String>();
+    String dependency1 = "one";
+    String dependency2 = "two";
+    dependencies.add(dependency1);
+    dependencies.add(dependency2);
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setConfigDependencies(dependencies);
+    assertEquals(dependencies, resolveComponent(info, parentInfo).getModuleInfo().getConfigDependencies());
+
+    // child has value set, parent value is null
+    info.setConfigDependencies(dependencies);
+    parentInfo.setConfigDependencies(null);
+    assertEquals(dependencies, resolveComponent(info, parentInfo).getModuleInfo().getConfigDependencies());
+
+    // value set in both parent and child; merge parent and child
+    List<String> dependencies2 = new ArrayList<String>();
+    String dependency3 = "two";
+    String dependency4 = "four";
+    dependencies2.add(dependency3);
+    dependencies2.add(dependency4);
+
+    info.setConfigDependencies(dependencies2);
+    parentInfo.setConfigDependencies(dependencies);
+
+    List<String> resolvedDependencies = resolveComponent(info, parentInfo).getModuleInfo().getConfigDependencies();
+    assertEquals(2, resolvedDependencies.size());
+    assertTrue(resolvedDependencies.contains(dependency3));
+    assertTrue(resolvedDependencies.contains(dependency4));
+  }
+
+  @Test
+  // merging of "client to update configs", whatever that means, is different than most other non-module merges
+  // in that the collections aren't merged if any "client to update configs" is specified in the child.
+  // So, the merged result is either the child collection or if null, the parent collection.
+  public void testResolve_ClientToUpdateConfigs() {
+    List<String> clientsToUpdate = new ArrayList<String>();
+    String client1 = "one";
+    String client2 = "two";
+    clientsToUpdate.add(client1);
+    clientsToUpdate.add(client2);
+
+    ComponentInfo info = new ComponentInfo();
+    ComponentInfo parentInfo = new ComponentInfo();
+
+    // parent has value set, child value is null
+    parentInfo.setClientsToUpdateConfigs(clientsToUpdate);
+    assertEquals(clientsToUpdate, resolveComponent(info, parentInfo).getModuleInfo().getClientsToUpdateConfigs());
+
+    // child has value set, parent value is null
+    info.setClientsToUpdateConfigs(clientsToUpdate);
+    parentInfo.setClientsToUpdateConfigs(null);
+    assertEquals(clientsToUpdate, resolveComponent(info, parentInfo).getModuleInfo().getClientsToUpdateConfigs());
+
+    // value set in both parent and child; merge parent and child
+    List<String> clientsToUpdate2 = new ArrayList<String>();
+    String client3 = "two";
+    String client4 = "four";
+    clientsToUpdate2.add(client3);
+    clientsToUpdate2.add(client4);
+
+    info.setClientsToUpdateConfigs(clientsToUpdate2);
+    parentInfo.setClientsToUpdateConfigs(clientsToUpdate);
+
+    List<String> resolvedClientsToUpdate = resolveComponent(info, parentInfo).getModuleInfo().getClientsToUpdateConfigs();
+    assertEquals(2, resolvedClientsToUpdate.size());
+    assertTrue(resolvedClientsToUpdate.contains(client3));
+    assertTrue(resolvedClientsToUpdate.contains(client4));
+  }
+
+  @Test
+  public void testGetId() {
+    ComponentInfo info = new ComponentInfo();
+    info.setName("foo");
+
+    ComponentModule component = new ComponentModule(info);
+    assertEquals("foo", component.getId());
+  }
+
+  @Test
+  public void testIsDeleted() {
+    // default value
+    ComponentInfo info = new ComponentInfo();
+    info.setName("foo");
+
+    ComponentModule component = new ComponentModule(info);
+    assertFalse(component.isDeleted());
+
+    // explicit value
+    info = new ComponentInfo();
+    info.setName("foo");
+    info.setDeleted(true);
+
+    component = new ComponentModule(info);
+    assertTrue(component.isDeleted());
+  }
+
+  private ComponentModule resolveComponent(ComponentInfo info, ComponentInfo parentInfo) {
+    info.setName("FOO");
+    parentInfo.setName("FOO");
+
+    ComponentModule component = new ComponentModule(info);
+    ComponentModule parentComponent = new ComponentModule(parentInfo);
+
+    component.resolve(parentComponent, Collections.<String, StackModule>emptyMap());
+
+    return component;
+  }
+
+  private void setPrivateField(Object o, String field, Object value) throws Exception{
+    Class<?> c = o.getClass();
+    Field f = c.getDeclaredField(field);
+    f.setAccessible(true);
+    f.set(o, value);
+  }
+}
+

http://git-wip-us.apache.org/repos/asf/ambari/blob/2fc7adec/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java
----------------------------------------------------------------------
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java b/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java
new file mode 100644
index 0000000..225213f
--- /dev/null
+++ b/ambari-server/src/test/java/org/apache/ambari/server/stack/ServiceModuleTest.java
@@ -0,0 +1,983 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ambari.server.stack;
+
+import org.apache.ambari.server.AmbariException;
+import org.apache.ambari.server.state.CommandScriptDefinition;
+import org.apache.ambari.server.state.ComponentInfo;
+import org.apache.ambari.server.state.CustomCommandDefinition;
+import org.apache.ambari.server.state.PropertyInfo;
+import org.apache.ambari.server.state.ServiceInfo;
+import org.apache.ambari.server.state.ServiceOsSpecific;
+import org.junit.Test;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.easymock.EasyMock.createNiceMock;
+import static org.easymock.EasyMock.createStrictMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * ServiceModule unit tests.
+ */
+public class ServiceModuleTest {
+
+  @Test
+  public void testResolve_Comment() throws Exception {
+    String comment = "test comment";
+
+    // comment specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setComment(comment);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(comment, service.getModuleInfo().getComment());
+
+    // comment specified in parent only
+    info.setComment(null);
+    parentInfo.setComment(comment);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(comment, service.getModuleInfo().getComment());
+
+    // set in both
+    info.setComment(comment);
+    parentInfo.setComment("other comment");
+
+    service = resolveService(info, parentInfo);
+    assertEquals(comment, service.getModuleInfo().getComment());
+  }
+
+  @Test
+  public void testResolve_DisplayName() throws Exception {
+    String displayName = "test_display_name";
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setDisplayName(displayName);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(displayName, service.getModuleInfo().getDisplayName());
+
+    // specified in parent only
+    info.setDisplayName(null);
+    parentInfo.setDisplayName(displayName);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(displayName, service.getModuleInfo().getDisplayName());
+
+    // specified in both
+    info.setDisplayName(displayName);
+    parentInfo.setDisplayName("other display name");
+
+    service = resolveService(info, parentInfo);
+    assertEquals(displayName, service.getModuleInfo().getDisplayName());
+  }
+
+  @Test
+  public void testResolve_RequiredServices() throws Exception {
+    List<String> requiredServices = new ArrayList<String>();
+    requiredServices.add("foo");
+    requiredServices.add("bar");
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setRequiredServices(requiredServices);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(requiredServices, service.getModuleInfo().getRequiredServices());
+
+    // specified in parent only
+    info.setRequiredServices(null);
+    parentInfo.setRequiredServices(requiredServices);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(requiredServices, service.getModuleInfo().getRequiredServices());
+
+    // specified in both
+    info.setRequiredServices(requiredServices);
+    parentInfo.setRequiredServices(Collections.singletonList("other"));
+
+    service = resolveService(info, parentInfo);
+    assertEquals(requiredServices, service.getModuleInfo().getRequiredServices());
+
+    // not set in either
+    info.setRequiredServices(null);
+    parentInfo.setRequiredServices(null);
+
+    service = resolveService(info, parentInfo);
+    assertTrue(service.getModuleInfo().getRequiredServices().isEmpty());
+  }
+
+  @Test
+  public void testResolve_RestartRequiredAfterChange() throws Exception {
+    Boolean isRestartRequired = true;
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setRestartRequiredAfterChange(isRestartRequired);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(isRestartRequired, service.getModuleInfo().isRestartRequiredAfterChange());
+
+    // specified in parent only
+    info.setRestartRequiredAfterChange(null);
+    parentInfo.setRestartRequiredAfterChange(isRestartRequired);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(isRestartRequired, service.getModuleInfo().isRestartRequiredAfterChange());
+
+    // specified in both
+    info.setRestartRequiredAfterChange(isRestartRequired);
+    parentInfo.setRestartRequiredAfterChange(false);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(isRestartRequired, service.getModuleInfo().isRestartRequiredAfterChange());
+  }
+
+  @Test
+  public void testResolve_MonitoringService() throws Exception {
+    Boolean isMonitoringService = true;
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setMonitoringService(isMonitoringService);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(isMonitoringService, service.getModuleInfo().isMonitoringService());
+
+    // specified in parent only
+    info.setMonitoringService(null);
+    parentInfo.setMonitoringService(isMonitoringService);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(isMonitoringService, service.getModuleInfo().isMonitoringService());
+
+    // specified in both
+    info.setMonitoringService(isMonitoringService);
+    parentInfo.setMonitoringService(false);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(isMonitoringService, service.getModuleInfo().isMonitoringService());
+  }
+
+  @Test
+  public void testResolve_OsSpecifics() throws Exception {
+    Map<String, ServiceOsSpecific> osSpecifics = new HashMap<String, ServiceOsSpecific>();
+    osSpecifics.put("foo", new ServiceOsSpecific());
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setOsSpecifics(osSpecifics);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(osSpecifics, service.getModuleInfo().getOsSpecifics());
+
+    // specified in parent only
+    info.setOsSpecifics(null);
+    parentInfo.setOsSpecifics(osSpecifics);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(osSpecifics, service.getModuleInfo().getOsSpecifics());
+
+    // specified in both
+    Map<String, ServiceOsSpecific> osSpecifics2 = new HashMap<String, ServiceOsSpecific>();
+    osSpecifics.put("bar", new ServiceOsSpecific());
+
+    info.setOsSpecifics(osSpecifics);
+    parentInfo.setOsSpecifics(osSpecifics2);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(osSpecifics, service.getModuleInfo().getOsSpecifics());
+  }
+
+  @Test
+  public void testResolve_CommandScript() throws Exception {
+    CommandScriptDefinition commandScript = new CommandScriptDefinition();
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setCommandScript(commandScript);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(commandScript, service.getModuleInfo().getCommandScript());
+
+    // specified in parent only
+    info.setCommandScript(null);
+    parentInfo.setCommandScript(commandScript);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(commandScript, service.getModuleInfo().getCommandScript());
+
+    // specified in both
+    CommandScriptDefinition commandScript2 = new CommandScriptDefinition();
+
+    info.setCommandScript(commandScript);
+    parentInfo.setCommandScript(commandScript2);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(commandScript, service.getModuleInfo().getCommandScript());
+  }
+
+  @Test
+  public void testResolve_ServicePackageFolder() throws Exception {
+    String servicePackageFolder = "packageDir";
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+
+    ServiceModule child = createServiceModule(info);
+    ServiceModule parent = createServiceModule(parentInfo);
+
+    // set in the module constructor from a value obtained from service directory
+    assertEquals("packageDir", child.getModuleInfo().getServicePackageFolder());
+    parent.getModuleInfo().setServicePackageFolder(null);
+
+    resolveService(child, parent);
+    assertEquals(servicePackageFolder, child.getModuleInfo().getServicePackageFolder());
+
+    // specified in parent only
+    child = createServiceModule(info);
+    parent = createServiceModule(parentInfo);
+    parent.getModuleInfo().setServicePackageFolder(servicePackageFolder);
+    child.getModuleInfo().setServicePackageFolder(null);
+
+    resolveService(child, parent);
+    assertEquals(servicePackageFolder, child.getModuleInfo().getServicePackageFolder());
+
+    // specified in both
+    child = createServiceModule(info);
+    parent = createServiceModule(parentInfo);
+    parent.getModuleInfo().setServicePackageFolder("someOtherDir");
+    child.getModuleInfo().setServicePackageFolder(servicePackageFolder);
+
+    resolveService(child, parent);
+    assertEquals(servicePackageFolder, child.getModuleInfo().getServicePackageFolder());
+  }
+
+  @Test
+  public void testResolve_MetricsFile() throws Exception {
+    File metricsFile = new File("testMetricsFile");
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+
+    ServiceModule child = createServiceModule(info);
+    ServiceModule parent = createServiceModule(parentInfo);
+
+    // set in the module constructor from a value obtained from service directory which is mocked
+    assertEquals(metricsFile, child.getModuleInfo().getMetricsFile());
+    parent.getModuleInfo().setMetricsFile(null);
+
+    resolveService(child, parent);
+    assertEquals(metricsFile, child.getModuleInfo().getMetricsFile());
+
+    // specified in parent only
+    child = createServiceModule(info);
+    parent = createServiceModule(parentInfo);
+    parent.getModuleInfo().setMetricsFile(metricsFile);
+    child.getModuleInfo().setMetricsFile(null);
+
+    resolveService(child, parent);
+    assertEquals(metricsFile, child.getModuleInfo().getMetricsFile());
+
+    // specified in both
+    child = createServiceModule(info);
+    parent = createServiceModule(parentInfo);
+    parent.getModuleInfo().setMetricsFile(new File("someOtherDir"));
+    child.getModuleInfo().setMetricsFile(metricsFile);
+
+    resolveService(child, parent);
+    assertEquals(metricsFile, child.getModuleInfo().getMetricsFile());
+  }
+
+  @Test
+  public void testResolve_AlertsFile() throws Exception {
+    File alertsFile = new File("testAlertsFile");
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+
+    ServiceModule child = createServiceModule(info);
+    ServiceModule parent = createServiceModule(parentInfo);
+
+    // set in the module constructor from a value obtained from service directory which is mocked
+    assertEquals(alertsFile, child.getModuleInfo().getAlertsFile());
+    parent.getModuleInfo().setAlertsFile(null);
+
+    resolveService(child, parent);
+    assertEquals(alertsFile, child.getModuleInfo().getAlertsFile());
+
+    // specified in parent only
+    child = createServiceModule(info);
+    parent = createServiceModule(parentInfo);
+    parent.getModuleInfo().setAlertsFile(alertsFile);
+    child.getModuleInfo().setAlertsFile(null);
+
+    resolveService(child, parent);
+    assertEquals(alertsFile, child.getModuleInfo().getAlertsFile());
+
+    // specified in both
+    child = createServiceModule(info);
+    parent = createServiceModule(parentInfo);
+    parent.getModuleInfo().setAlertsFile(new File("someOtherDir"));
+    child.getModuleInfo().setAlertsFile(alertsFile);
+
+    resolveService(child, parent);
+    assertEquals(alertsFile, child.getModuleInfo().getAlertsFile());
+  }
+
+  @Test
+  public void testResolve_CustomCommands() throws Exception {
+    List<CustomCommandDefinition> customCommands = new ArrayList<CustomCommandDefinition>();
+    CustomCommandDefinition cmd1 = new CustomCommandDefinition();
+    setPrivateField(cmd1, "name", "cmd1");
+    setPrivateField(cmd1, "background", false);
+    CustomCommandDefinition cmd2 = new CustomCommandDefinition();
+    setPrivateField(cmd2, "name", "cmd2");
+    customCommands.add(cmd1);
+    customCommands.add(cmd2);
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setCustomCommands(customCommands);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(customCommands, service.getModuleInfo().getCustomCommands());
+
+    // specified in parent only
+    info.setCustomCommands(null);
+    parentInfo.setCustomCommands(customCommands);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(customCommands, service.getModuleInfo().getCustomCommands());
+
+    // specified in both
+    List<CustomCommandDefinition> parentCustomCommands = new ArrayList<CustomCommandDefinition>();
+    CustomCommandDefinition cmd3 = new CustomCommandDefinition();
+    setPrivateField(cmd3, "name", "cmd1");
+    setPrivateField(cmd3, "background", true);
+    CustomCommandDefinition cmd4 = new CustomCommandDefinition();
+    setPrivateField(cmd4, "name", "cmd4");
+    parentCustomCommands.add(cmd3);
+    parentCustomCommands.add(cmd4);
+
+    info.setCustomCommands(customCommands);
+    parentInfo.setCustomCommands(parentCustomCommands);
+
+    service = resolveService(info, parentInfo);
+    Collection<CustomCommandDefinition> mergedCommands =  service.getModuleInfo().getCustomCommands();
+    assertEquals(3, mergedCommands.size());
+    assertTrue(mergedCommands.contains(cmd2));
+    assertTrue(mergedCommands.contains(cmd3));
+    assertTrue(mergedCommands.contains(cmd4));
+
+    // not set in either
+    info.setCustomCommands(null);
+    parentInfo.setCustomCommands(null);
+
+    service = resolveService(info, parentInfo);
+    assertTrue(service.getModuleInfo().getCustomCommands().isEmpty());
+  }
+
+  @Test
+  public void testResolve_ConfigDependencies() throws Exception {
+    List<String> configDependencies = new ArrayList<String>();
+    configDependencies.add("foo");
+    configDependencies.add("bar");
+
+    // specified in child only
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+    info.setConfigDependencies(configDependencies);
+
+    ServiceModule service = resolveService(info, parentInfo);
+    assertEquals(configDependencies, service.getModuleInfo().getConfigDependencies());
+
+    // specified in parent only
+    info.setConfigDependencies(null);
+    parentInfo.setConfigDependencies(configDependencies);
+
+    service = resolveService(info, parentInfo);
+    assertEquals(configDependencies, service.getModuleInfo().getConfigDependencies());
+
+    // specified in both
+    List<String> parentCustomCommands = new ArrayList<String>();
+    parentCustomCommands.add("bar");
+    parentCustomCommands.add("other");
+
+    info.setConfigDependencies(configDependencies);
+    parentInfo.setConfigDependencies(parentCustomCommands);
+
+    service = resolveService(info, parentInfo);
+    Collection<String> mergedConfigDependencies =  service.getModuleInfo().getConfigDependencies();
+    assertEquals(3, mergedConfigDependencies.size());
+    assertTrue(mergedConfigDependencies.contains("foo"));
+    assertTrue(mergedConfigDependencies.contains("bar"));
+    assertTrue(mergedConfigDependencies.contains("other"));
+
+    // not set in either
+    info.setConfigDependencies(null);
+    parentInfo.setConfigDependencies(null);
+
+    service = resolveService(info, parentInfo);
+    assertTrue(service.getModuleInfo().getConfigDependencies().isEmpty());
+  }
+
+  @Test
+  public void testResolve_Components() throws Exception {
+    // resolve should merge the child component collections
+    // components 1, 2 and XX are set on the parent
+    // components 1, 4 and XX are set on the child
+    // component XX is marked for delete on the child and shouldn't be included
+    // component 1 should be merged
+    // both non-intersecting components 2 and 4 should be included
+    ComponentInfo info1 = new ComponentInfo();
+    info1.setName("1");
+    info1.setCardinality("ALL");
+    ComponentInfo info2 = new ComponentInfo();
+    info2.setName("2");
+    ComponentInfo XX = new ComponentInfo();
+    XX.setName("XX");
+
+    ComponentInfo info3 = new ComponentInfo();
+    // overlaps with info1
+    info3.setName("1");
+    info3.setCategory("category");
+    ComponentInfo info4 = new ComponentInfo();
+    info4.setName("4");
+    ComponentInfo info5 = new ComponentInfo();
+    // overlaps with componentToBeDeleted
+    info5.setName("XX");
+    info5.setDeleted(true);
+
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+
+    //todo: getComponents() should return a protective copy, but for now there is no set/add method
+    List<ComponentInfo> childComponents = info.getComponents();
+    childComponents.add(info3);
+    childComponents.add(info4);
+    childComponents.add(info5);
+
+    List<ComponentInfo> parentComponents = parentInfo.getComponents();
+    parentComponents.add(info1);
+    parentComponents.add(info2);
+
+    ServiceModule child = createServiceModule(info);
+    ServiceModule parent = createServiceModule(parentInfo);
+
+    resolveService(child, parent);
+
+    List<ComponentInfo> components = child.getModuleInfo().getComponents();
+    assertEquals(3, components.size());
+
+    Map<String, ComponentInfo> mergedComponents = new HashMap<String, ComponentInfo>();
+    for (ComponentInfo component : components) {
+      mergedComponents.put(component.getName(), component);
+    }
+    assertTrue(mergedComponents.containsKey("1"));
+    assertTrue(mergedComponents.containsKey("2"));
+    assertTrue(mergedComponents.containsKey("4"));
+
+    // ensure that overlapping components were merged.
+    //don't test all properties, this is done in ComponentModuleTest
+    assertEquals("ALL", mergedComponents.get("1").getCardinality());
+    assertEquals("category", mergedComponents.get("1").getCategory());
+  }
+
+  @Test
+  public void testResolve_Configuration__properties() throws Exception {
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+
+    // child configurations
+    //FOO
+    Collection<PropertyInfo> childFooProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo childProp1 = new PropertyInfo();
+    childProp1.setName("childName1");
+    childProp1.setValue("childVal1");
+    childFooProperties.add(childProp1);
+
+    //BAR : Doesn't inherit parents BAR due to attribute Supports.DO_NOT_EXTEND
+    Collection<PropertyInfo> childBarProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo childProp2 = new PropertyInfo();
+    childProp2.setName("childName2");
+    childProp2.setValue("childVal2");
+    childBarProperties.add(childProp2);
+
+    // add attributes for BAR
+    Map<String, String> attributes = new HashMap<String, String>();
+    attributes.put(ConfigurationInfo.Supports.DO_NOT_EXTEND.getXmlAttributeName(), "true");
+
+    // create child config modules
+    ConfigurationModule childConfigModule1 = createConfigurationModule("FOO", childFooProperties);
+    ConfigurationModule childConfigModule2 = createConfigurationModule("BAR", childBarProperties, attributes);
+    Collection<ConfigurationModule> childModules = new ArrayList<ConfigurationModule>();
+    childModules.add(childConfigModule1);
+    childModules.add(childConfigModule2);
+
+    // parent configurations
+    //FOO
+    Collection<PropertyInfo> parentFooProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo parentProp1 = new PropertyInfo();
+    parentProp1.setName("parentName1");
+    parentProp1.setValue("parentVal1");
+    parentFooProperties.add(parentProp1);
+    PropertyInfo parentProp12 = new PropertyInfo();
+    // overwritten by child
+    parentProp12.setName("childName1");
+    parentProp12.setValue("parentVal1");
+    parentFooProperties.add(parentProp12);
+
+    //BAR
+    Collection<PropertyInfo> parentBarProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo parentProp2 = new PropertyInfo();
+    parentProp2.setName("parentName2");
+    parentProp2.setValue("parentVal2");
+    parentBarProperties.add(parentProp2);
+
+    //OTHER
+    Collection<PropertyInfo> parentOtherProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo parentProp3 = new PropertyInfo();
+    parentProp3.setName("parentName3");
+    parentProp3.setValue("parentVal3");
+    parentOtherProperties.add(parentProp3);
+
+    // create parent config modules
+    ConfigurationModule parentConfigModule1 = createConfigurationModule("FOO", parentFooProperties);
+    ConfigurationModule parentConfigModule2 = createConfigurationModule("BAR", parentBarProperties);
+    ConfigurationModule parentConfigModule3 = createConfigurationModule("OTHER", parentOtherProperties);
+    Collection<ConfigurationModule> parentModules = new ArrayList<ConfigurationModule>();
+    parentModules.add(parentConfigModule1);
+    parentModules.add(parentConfigModule2);
+    parentModules.add(parentConfigModule3);
+
+    // create service modules
+    ServiceModule child = createServiceModule(info, childModules);
+    ServiceModule parent = createServiceModule(parentInfo, parentModules);
+
+    // resolve child with parent
+    resolveService(child, parent);
+
+    // assertions
+    List<PropertyInfo> mergedProperties = child.getModuleInfo().getProperties();
+    assertEquals(4, mergedProperties.size());
+
+    Map<String, PropertyInfo> mergedPropertyMap = new HashMap<String, PropertyInfo>();
+    for (PropertyInfo prop : mergedProperties) {
+      mergedPropertyMap.put(prop.getName(), prop);
+    }
+
+    // filename is null for all props because that is set in ConfigurationDirectory which is mocked
+    assertEquals("childVal1", mergedPropertyMap.get("childName1").getValue());
+    assertEquals("childVal2", mergedPropertyMap.get("childName2").getValue());
+    assertEquals("parentVal1", mergedPropertyMap.get("parentName1").getValue());
+    assertEquals("parentVal3", mergedPropertyMap.get("parentName3").getValue());
+
+    Map<String, Map<String, Map<String, String>>> childAttributes = child.getModuleInfo().getConfigTypeAttributes();
+    Map<String, Map<String, Map<String, String>>> parentAttributes = parent.getModuleInfo().getConfigTypeAttributes();
+
+    assertEquals(3, childAttributes.size());
+    assertAttributes(childAttributes.get("FOO"), Collections.<String, String>emptyMap());
+    assertAttributes(childAttributes.get("BAR"), attributes);
+    assertAttributes(childAttributes.get("OTHER"), Collections.<String, String>emptyMap());
+
+    assertEquals(3, parentAttributes.size());
+    assertAttributes(parentAttributes.get("FOO"), Collections.<String, String>emptyMap());
+    assertAttributes(parentAttributes.get("BAR"), Collections.<String, String>emptyMap());
+    assertAttributes(parentAttributes.get("OTHER"), Collections.<String, String>emptyMap());
+  }
+
+  @Test
+  public void testResolve_Configuration__attributes() throws Exception {
+    ServiceInfo info = new ServiceInfo();
+    ServiceInfo parentInfo = new ServiceInfo();
+
+    // child configurations
+    //FOO
+    Collection<PropertyInfo> childFooProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo childProp1 = new PropertyInfo();
+    childProp1.setName("childName1");
+    childProp1.setValue("childVal1");
+    childFooProperties.add(childProp1);
+
+    // add attributes for parent FOO
+    Map<String, String> childFooAttributes = new HashMap<String, String>();
+    // override parents value
+    childFooAttributes.put(ConfigurationInfo.Supports.ADDING_FORBIDDEN.getXmlAttributeName(), "false");
+
+    // create child config modules
+    ConfigurationModule childConfigModule1 = createConfigurationModule("FOO", childFooProperties, childFooAttributes);
+    Collection<ConfigurationModule> childModules = new ArrayList<ConfigurationModule>();
+    childModules.add(childConfigModule1);
+
+    // parent configurations
+    //FOO
+    Collection<PropertyInfo> parentFooProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo parentProp1 = new PropertyInfo();
+    parentProp1.setName("parentName1");
+    parentProp1.setValue("parentVal1");
+    parentFooProperties.add(parentProp1);
+
+    // add attributes for parent FOO
+    Map<String, String> parentFooAttributes = new HashMap<String, String>();
+    // child will inherit
+    parentFooAttributes.put(ConfigurationInfo.Supports.FINAL.getXmlAttributeName(), "true");
+    // child will override
+    parentFooAttributes.put(ConfigurationInfo.Supports.ADDING_FORBIDDEN.getXmlAttributeName(), "true");
+
+    //BAR
+    Collection<PropertyInfo> parentBarProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo parentProp2 = new PropertyInfo();
+    parentProp2.setName("parentName2");
+    parentProp2.setValue("parentVal2");
+    parentBarProperties.add(parentProp2);
+
+
+    // create parent config modules
+    ConfigurationModule parentConfigModule1 = createConfigurationModule("FOO", parentFooProperties, parentFooAttributes);
+    ConfigurationModule parentConfigModule2 = createConfigurationModule("BAR", parentBarProperties);
+    Collection<ConfigurationModule> parentModules = new ArrayList<ConfigurationModule>();
+    parentModules.add(parentConfigModule1);
+    parentModules.add(parentConfigModule2);
+
+    // create service modules
+    ServiceModule child = createServiceModule(info, childModules);
+    ServiceModule parent = createServiceModule(parentInfo, parentModules);
+
+    // resolve child with parent
+    resolveService(child, parent);
+
+    // assertions
+    Map<String, Map<String, Map<String, String>>> childTypeAttributes = child.getModuleInfo().getConfigTypeAttributes();
+    Map<String, Map<String, Map<String, String>>> parentTypeAttributes = parent.getModuleInfo().getConfigTypeAttributes();
+    assertTrue(childTypeAttributes.containsKey("FOO"));
+    Map<String, Map<String, String>> mergedChildFooAttributes = childTypeAttributes.get("FOO");
+    assertTrue(mergedChildFooAttributes.containsKey(ConfigurationInfo.Supports.KEYWORD));
+    // inherited value
+    assertEquals("true", mergedChildFooAttributes.get(ConfigurationInfo.Supports.KEYWORD).
+        get(ConfigurationInfo.Supports.valueOf("FINAL").getPropertyName()));
+    // overridden value
+    assertEquals("false", mergedChildFooAttributes.get(ConfigurationInfo.Supports.KEYWORD).
+        get(ConfigurationInfo.Supports.valueOf("ADDING_FORBIDDEN").getPropertyName()));
+
+    assertEquals(2, childTypeAttributes.size());
+
+    assertEquals(2, parentTypeAttributes.size());
+    assertAttributes(parentTypeAttributes.get("FOO"), parentFooAttributes);
+    assertAttributes(parentTypeAttributes.get("BAR"), Collections.<String, String>emptyMap());
+  }
+
+  @Test
+  public void testResolve_Configuration__ExcludedTypes() throws Exception {
+    ServiceInfo info = new ServiceInfo();
+    info.setExcludedConfigTypes(Collections.singleton("BAR"));
+
+    //FOO
+    Collection<PropertyInfo> fooProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo prop1 = new PropertyInfo();
+    prop1.setName("name1");
+    prop1.setValue("val1");
+    fooProperties.add(prop1);
+    PropertyInfo prop2 = new PropertyInfo();
+    prop2.setName("name2");
+    prop2.setValue("val2");
+    fooProperties.add(prop2);
+
+    //BAR
+    Collection<PropertyInfo> barProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo prop3 = new PropertyInfo();
+    prop3.setName("name1");
+    prop3.setValue("val3");
+    barProperties.add(prop3);
+
+    //OTHER
+    Collection<PropertyInfo> otherProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo prop4 = new PropertyInfo();
+    prop4.setName("name1");
+    prop4.setValue("val4");
+    otherProperties.add(prop4);
+
+    ConfigurationModule configModule1 = createConfigurationModule("FOO", fooProperties);
+    ConfigurationModule configModule2 = createConfigurationModule("BAR", barProperties);
+    ConfigurationModule configModule3 = createConfigurationModule("OTHER", otherProperties);
+    Collection<ConfigurationModule> configModules = new ArrayList<ConfigurationModule>();
+    configModules.add(configModule1);
+    configModules.add(configModule2);
+    configModules.add(configModule3);
+
+    ServiceModule service = createServiceModule(info, configModules);
+
+    List<PropertyInfo> properties = service.getModuleInfo().getProperties();
+    assertEquals(3, properties.size());
+
+    Map<String, Map<String, Map<String, String>>> attributes = service.getModuleInfo().getConfigTypeAttributes();
+    assertEquals(2, attributes.size());
+    assertTrue(attributes.containsKey("FOO"));
+    assertTrue(attributes.containsKey("OTHER"));
+  }
+
+  @Test
+  public void testResolve_Configuration__ExcludedTypes__ParentType() throws Exception {
+    // child
+    ServiceInfo info = new ServiceInfo();
+    info.setExcludedConfigTypes(Collections.singleton("BAR"));
+
+    //FOO
+    Collection<PropertyInfo> fooProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo prop1 = new PropertyInfo();
+    prop1.setName("name1");
+    prop1.setValue("val1");
+    fooProperties.add(prop1);
+    PropertyInfo prop2 = new PropertyInfo();
+    prop2.setName("name2");
+    prop2.setValue("val2");
+    fooProperties.add(prop2);
+
+    ConfigurationModule childConfigModule = createConfigurationModule("FOO", fooProperties);
+    Collection<ConfigurationModule> childConfigModules = new ArrayList<ConfigurationModule>();
+    childConfigModules.add(childConfigModule);
+
+    // parent
+    ServiceInfo parentInfo = new ServiceInfo();
+
+    //BAR
+    Collection<PropertyInfo> barProperties = new ArrayList<PropertyInfo>();
+    PropertyInfo prop3 = new PropertyInfo();
+    prop3.setName("name1");
+    prop3.setValue("val3");
+    barProperties.add(prop3);
+
+    ConfigurationModule parentConfigModule = createConfigurationModule("BAR", barProperties);
+    Collection<ConfigurationModule> parentConfigModules = new ArrayList<ConfigurationModule>();
+    parentConfigModules.add(parentConfigModule);
+
+    // create service modules
+    ServiceModule service = createServiceModule(info, childConfigModules);
+    ServiceModule parentService = createServiceModule(parentInfo, parentConfigModules);
+    // resolve child with parent
+    resolveService(service, parentService);
+    // assertions
+    List<PropertyInfo> properties = service.getModuleInfo().getProperties();
+    assertEquals(2, properties.size());
+
+    Map<String, Map<String, Map<String, String>>> attributes = service.getModuleInfo().getConfigTypeAttributes();
+    assertEquals(1, attributes.size());
+    assertTrue(attributes.containsKey("FOO"));
+
+    Map<String, Map<String, Map<String, String>>> parentAttributes = parentService.getModuleInfo().getConfigTypeAttributes();
+    assertEquals(1, parentAttributes.size());
+    assertTrue(parentAttributes.containsKey("BAR"));
+  }
+
+  @Test
+  public void testServiceCheckRegistered() throws Exception {
+    ServiceInfo info = new ServiceInfo();
+    info.setName("service1");
+    info.setCommandScript(createNiceMock(CommandScriptDefinition.class));
+
+    StackContext context = createStackContext(info.getName(), true);
+    ServiceModule service = createServiceModule(info, Collections.<ConfigurationModule>emptySet(), context);
+    service.finalizeModule();
+
+    verify(context);
+  }
+
+  @Test
+  public void testServiceCheckNotRegisteredForDeletedService() throws Exception {
+    ServiceInfo info = new ServiceInfo();
+    info.setName("service1");
+    info.setCommandScript(createNiceMock(CommandScriptDefinition.class));
+    info.setDeleted(true);
+
+    StackContext context = createStackContext(info.getName(), false);
+    ServiceModule service = createServiceModule(info, Collections.<ConfigurationModule>emptySet(), context);
+    service.finalizeModule();
+
+    verify(context);
+  }
+
+  private ServiceModule createServiceModule(ServiceInfo serviceInfo) {
+    String configType = "type1";
+
+    if (serviceInfo.getName() == null) {
+      serviceInfo.setName("service1");
+    }
+
+    StackContext context = createStackContext(serviceInfo.getName(), true);
+    // no config props
+    ConfigurationInfo configInfo = createConfigurationInfo(Collections.<PropertyInfo>emptyList(),
+        Collections.<String, String>emptyMap());
+
+    ConfigurationModule module = createConfigurationModule(configType, configInfo);
+    ConfigurationDirectory configDirectory = createConfigurationDirectory(Collections.singletonList(module));
+    ServiceDirectory serviceDirectory = createServiceDirectory(serviceInfo.getConfigDir(), configDirectory);
+
+    return createServiceModule(context, serviceInfo, serviceDirectory);
+  }
+
+  private ServiceModule createServiceModule(ServiceInfo serviceInfo,
+                                            Collection<ConfigurationModule> configurations,
+                                            StackContext context) {
+
+    if (serviceInfo.getName() == null) {
+      serviceInfo.setName("service1");
+    }
+
+    ConfigurationDirectory configDirectory = createConfigurationDirectory(configurations);
+    ServiceDirectory serviceDirectory = createServiceDirectory(serviceInfo.getConfigDir(), configDirectory);
+
+    return createServiceModule(context, serviceInfo, serviceDirectory);
+  }
+
+  private ServiceModule createServiceModule(ServiceInfo serviceInfo, Collection<ConfigurationModule> configurations) {
+    String serviceName = serviceInfo.getName();
+
+    if (serviceInfo.getName() == null) {
+      serviceInfo.setName("service1");
+    }
+
+    return createServiceModule(serviceInfo, configurations, createStackContext(serviceName, true));
+  }
+
+  private ServiceModule createServiceModule(StackContext context, ServiceInfo serviceInfo,
+                                            ServiceDirectory serviceDirectory) {
+
+    return new ServiceModule(context, serviceInfo, serviceDirectory);
+  }
+
+  private ServiceDirectory createServiceDirectory(String dir, ConfigurationDirectory configDir) {
+
+    ServiceDirectory serviceDirectory = createNiceMock(ServiceDirectory.class);
+
+    expect(serviceDirectory.getConfigurationDirectory(dir)).andReturn(configDir).anyTimes();
+    expect(serviceDirectory.getMetricsFile()).andReturn(new File("testMetricsFile")).anyTimes();
+    expect(serviceDirectory.getAlertsFile()).andReturn(new File("testAlertsFile")).anyTimes();
+    expect(serviceDirectory.getPackageDir()).andReturn("packageDir").anyTimes();
+    replay(serviceDirectory);
+
+    return serviceDirectory;
+  }
+
+  private ConfigurationDirectory createConfigurationDirectory(Collection<ConfigurationModule> modules) {
+    ConfigurationDirectory configDir = createNiceMock(ConfigurationDirectory.class);
+
+    expect(configDir.getConfigurationModules()).andReturn(modules).anyTimes();
+    replay(configDir);
+
+    return configDir;
+  }
+
+  private ConfigurationModule createConfigurationModule(String configType, ConfigurationInfo info) {
+    return new ConfigurationModule(configType, info);
+  }
+
+  private ConfigurationModule createConfigurationModule(String configType, Collection<PropertyInfo> properties) {
+    ConfigurationInfo info = new ConfigurationInfo(properties, Collections.<String, String>emptyMap());
+    return new ConfigurationModule(configType, info);
+  }
+
+  private ConfigurationModule createConfigurationModule(String configType,
+                                                        Collection<PropertyInfo> properties,
+                                                        Map<String, String> attributes) {
+
+    ConfigurationInfo info = new ConfigurationInfo(properties, attributes);
+    return new ConfigurationModule(configType, info);
+  }
+
+  private ConfigurationInfo createConfigurationInfo(Collection<PropertyInfo> properties,
+                                                    Map<String, String> attributes) {
+
+    return new ConfigurationInfo(properties, attributes);
+  }
+
+  private StackContext createStackContext(String serviceName, boolean expectServiceRegistration) {
+    StackContext context = createStrictMock(StackContext.class);
+
+    if (expectServiceRegistration) {
+      context.registerServiceCheck(serviceName);
+    }
+    replay(context);
+
+    return context;
+  }
+
+  private ServiceModule resolveService(ServiceInfo info, ServiceInfo parentInfo) throws AmbariException {
+    ServiceModule service = createServiceModule(info);
+    ServiceModule parentService = createServiceModule(parentInfo);
+
+    resolveService(service, parentService);
+    return service;
+  }
+
+  private void resolveService(ServiceModule service, ServiceModule parent) throws AmbariException {
+    service.resolve(parent, Collections.<String, StackModule>emptyMap());
+    // during runtime this would be called by the Stack module when it's resolve completed
+    service.finalizeModule();
+    parent.finalizeModule();
+  }
+
+  private void assertAttributes(Map<String, Map<String, String>> mergedAttributes, Map<String, String> specifiedAttributes) {
+    assertEquals(1, mergedAttributes.size()); // only supports
+    Map<String, String> supportsAttributes = mergedAttributes.get(ConfigurationInfo.Supports.KEYWORD);
+    assertEquals(ConfigurationInfo.Supports.values().length, supportsAttributes.size());
+    for (Map.Entry<String, String> attribute : supportsAttributes.entrySet()) {
+      String attributeName = attribute.getKey();
+      String attributeValue = attribute.getValue();
+
+      //need to call toUpper() because propertyName is name().toLowerCase()
+      ConfigurationInfo.Supports s = ConfigurationInfo.Supports.valueOf(attributeName.toUpperCase());
+      String specifiedVal = specifiedAttributes.get(s.getXmlAttributeName());
+      if (specifiedVal != null) {
+        assertEquals(specifiedVal, attributeValue);
+      } else {
+        assertEquals(s.getDefaultValue(), attributeValue);
+      }
+    }
+  }
+
+  private void setPrivateField(Object o, String field, Object value) throws Exception{
+    Class<?> c = o.getClass();
+    Field f = c.getDeclaredField(field);
+    f.setAccessible(true);
+    f.set(o, value);    }
+}