/*
 * 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.api.services;

import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.File;
import java.io.FileReader;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.UUID;

import jakarta.persistence.EntityManager;

import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.H2DatabaseCleaner;
import org.apache.ambari.server.StackAccessException;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.internal.DeleteHostComponentStatusMetaData;
import org.apache.ambari.server.controller.spi.Resource;
import org.apache.ambari.server.events.publishers.AmbariEventPublisher;
import org.apache.ambari.server.metadata.ActionMetadata;
import org.apache.ambari.server.metadata.AmbariServiceAlertDefinitions;
import org.apache.ambari.server.mpack.MpackManagerFactory;
import org.apache.ambari.server.orm.GuiceJpaInitializer;
import org.apache.ambari.server.orm.InMemoryDefaultTestModule;
import org.apache.ambari.server.orm.OrmTestHelper;
import org.apache.ambari.server.orm.dao.AlertDefinitionDAO;
import org.apache.ambari.server.orm.dao.MetainfoDAO;
import org.apache.ambari.server.orm.entities.AlertDefinitionEntity;
import org.apache.ambari.server.orm.entities.RepositoryVersionEntity;
import org.apache.ambari.server.stack.StackManager;
import org.apache.ambari.server.stack.StackManagerFactory;
import org.apache.ambari.server.state.AutoDeployInfo;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.ComponentInfo;
import org.apache.ambari.server.state.CustomCommandDefinition;
import org.apache.ambari.server.state.DependencyInfo;
import org.apache.ambari.server.state.OperatingSystemInfo;
import org.apache.ambari.server.state.PropertyDependencyInfo;
import org.apache.ambari.server.state.PropertyInfo;
import org.apache.ambari.server.state.RepositoryInfo;
import org.apache.ambari.server.state.ServiceInfo;
import org.apache.ambari.server.state.StackId;
import org.apache.ambari.server.state.StackInfo;
import org.apache.ambari.server.state.alert.AlertDefinition;
import org.apache.ambari.server.state.alert.AlertDefinitionFactory;
import org.apache.ambari.server.state.alert.MetricSource;
import org.apache.ambari.server.state.alert.PortSource;
import org.apache.ambari.server.state.alert.Reporting;
import org.apache.ambari.server.state.alert.Source;
import org.apache.ambari.server.state.alert.SourceType;
import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosDescriptorFactory;
import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptorFactory;
import org.apache.ambari.server.state.repository.VersionDefinitionXml;
import org.apache.ambari.server.state.stack.MetricDefinition;
import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.utils.EventBusSynchronizer;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.util.Modules;

import junit.framework.Assert;

public class AmbariMetaInfoTest {

  private static final String STACK_NAME_HDP = "HDP";
  private static final String STACK_VERSION_HDP = "0.1";
  private static final String EXT_STACK_NAME = "2.0.6";
  private static final String STACK_VERSION_HDP_02 = "0.2";
  private static final String STACK_MINIMAL_VERSION_HDP = "0.0";
  private static final String SERVICE_NAME_HDFS = "HDFS";
  private static final String SERVICE_NAME_MAPRED2 = "MAPREDUCE2";
  private static final String SERVICE_COMPONENT_NAME = "NAMENODE";
  private static final String OS_TYPE = "centos5";
  private static final String REPO_ID = "HDP-UTILS-1.1.0.15";
  private static final String PROPERTY_NAME = "hbase.regionserver.msginterval";
  private static final String SHARED_PROPERTY_NAME = "content";

  private static final String NON_EXT_VALUE = "XXX";

  private static final int REPOS_CNT = 3;
  private static final int PROPERTIES_CNT = 64;
  private static final int OS_CNT = 4;

  private static TestAmbariMetaInfo metaInfo = null;
  private final static Logger LOG = LoggerFactory.getLogger(AmbariMetaInfoTest.class);
  private static final String FILE_NAME = "hbase-site.xml";
  private static final String HADOOP_ENV_FILE_NAME = "hadoop-env.xml";
  private static final String HDFS_LOG4J_FILE_NAME = "hdfs-log4j.xml";

  //private Injector injector;

  //todo: add fail() for cases where an exception is expected such as getService, getComponent ...


  @Rule
  public TemporaryFolder tmpFolder = new TemporaryFolder();

  @BeforeClass
  public static void beforeClass() throws Exception {
    File stacks = new File("src/test/resources/stacks");
    File version = new File("src/test/resources/version");
    File resourcesRoot = new File("src/test/resources/");

    if (System.getProperty("os.name").contains("Windows")) {
      stacks = new File(ClassLoader.getSystemClassLoader().getResource("stacks").getPath());
      version = new File(new File(ClassLoader.getSystemClassLoader().getResource("").getPath()).getParent(), "version");
    }
    metaInfo = createAmbariMetaInfo(stacks, version, resourcesRoot);
  }

  @AfterClass
  public static void tearDown() throws Exception {
    H2DatabaseCleaner.clearDatabase(metaInfo.injector.getProvider(EntityManager.class).get());
  }

  public class MockModule extends AbstractModule {
    @Override
    protected void configure() {
      bind(ActionMetadata.class);
    }
  }


  @Test
  public void getRestartRequiredServicesNames() throws AmbariException {
    Set<String> res = metaInfo.getRestartRequiredServicesNames(STACK_NAME_HDP, "2.0.7");
    assertEquals(1, res.size());
  }

  @Test
  public void testGetRackSensitiveServicesNames() throws AmbariException {
    Set<String> res = metaInfo.getRackSensitiveServicesNames(STACK_NAME_HDP, "2.0.7");
    assertEquals(1, res.size());
    assertEquals("HDFS", res.iterator().next());
  }

  @Test
  public void getComponentsByService() throws AmbariException {
    List<ComponentInfo> components = metaInfo.getComponentsByService(
        STACK_NAME_HDP, STACK_VERSION_HDP, SERVICE_NAME_HDFS);
    assertNotNull(components);
    assertTrue(components.size() > 0);
  }

  @Test
  public void getLogs() throws AmbariException {
    ComponentInfo component;

    component = metaInfo.getComponent(STACK_NAME_HDP, "2.1.1", SERVICE_NAME_HDFS, "NAMENODE");
    assertNotNull(component);
    assertNotNull(component.getLogs());
    assertTrue(component.getLogs().size() == 2);
    assertEquals(component.getLogs().get(0).getLogId(), "hdfs_namenode");
    assertEquals(component.getLogs().get(1).getLogId(), "hdfs_audit");
    assertTrue(component.getLogs().get(0).isPrimary());
    assertFalse(component.getLogs().get(1).isPrimary());

    component = metaInfo.getComponent(STACK_NAME_HDP, "2.1.1", SERVICE_NAME_HDFS, "DATANODE");
    assertNotNull(component);
    assertNotNull(component.getLogs());
    assertTrue(component.getLogs().size() == 1);
    assertEquals(component.getLogs().get(0).getLogId(), "hdfs_datanode");
    assertTrue(component.getLogs().get(0).isPrimary());

    component = metaInfo.getComponent(STACK_NAME_HDP, "2.1.1", SERVICE_NAME_HDFS, "HDFS_CLIENT");
    assertNotNull(component);
    assertNotNull(component.getLogs());
    assertTrue(component.getLogs().isEmpty());
  }

  @Test
  public void getRepository() throws AmbariException {
    Map<String, List<RepositoryInfo>> repository = metaInfo.getRepository(
        STACK_NAME_HDP, STACK_VERSION_HDP);
    assertNotNull(repository);
    assertFalse(repository.get("centos5").isEmpty());
    assertFalse(repository.get("centos6").isEmpty());
  }

  @Test
  @Ignore
  public void testGetRepositoryDefault() throws Exception {
    // Scenario: user has internet and does nothing to repos via api
    // use the latest
    String buildDir = tmpFolder.getRoot().getAbsolutePath();
    AmbariMetaInfo ambariMetaInfo = setupTempAmbariMetaInfo(buildDir);
    // The current stack already has (HDP, 2.1.1, redhat6) with valid latest
    // url
    ambariMetaInfo.init();

    waitForAllReposToBeResolved(ambariMetaInfo);

    List<RepositoryInfo> redhat6Repo = ambariMetaInfo.getRepositories(
        STACK_NAME_HDP, "2.1.1", "redhat6");
    assertNotNull(redhat6Repo);
    for (RepositoryInfo ri : redhat6Repo) {
      if (STACK_NAME_HDP.equals(ri.getRepoName())) {
        assertTrue(ri.getBaseUrl().equals(ri.getDefaultBaseUrl()));
      }
    }
  }

  @Test
  public void testGetRepositoryNoInternetDefault() throws Exception {
    // Scenario: user has no internet and does nothing to repos via api
    // use the default
    String buildDir = tmpFolder.getRoot().getAbsolutePath();
    setupTempAmbariMetaInfoDirs(buildDir);
    // The current stack already has (HDP, 2.1.1, redhat6).

    // Deleting the json file referenced by the latestBaseUrl to simulate No
    // Internet.
    File latestUrlFile = new File(buildDir, "ambari-metaInfo/HDP/2.1.1/repos/hdp.json");
    if (System.getProperty("os.name").contains("Windows")) {
      latestUrlFile.deleteOnExit();
    }
    else {
      FileUtils.deleteQuietly(latestUrlFile);
      assertTrue(!latestUrlFile.exists());
    }
    AmbariMetaInfo ambariMetaInfo = setupTempAmbariMetaInfoExistingDirs(buildDir);

    List<RepositoryInfo> redhat6Repo = ambariMetaInfo.getRepositories(
        STACK_NAME_HDP, "2.1.1", "redhat6");
    assertNotNull(redhat6Repo);
    for (RepositoryInfo ri : redhat6Repo) {
      if (STACK_NAME_HDP.equals(ri.getRepoName())) {
        // baseUrl should be same as defaultBaseUrl since No Internet to load the
        // latestBaseUrl from the json file.
        assertEquals(ri.getBaseUrl(), ri.getDefaultBaseUrl());
      }
    }
  }

  @Test
  public void isSupportedStack() throws AmbariException {
    boolean supportedStack = metaInfo.isSupportedStack(STACK_NAME_HDP,
        STACK_VERSION_HDP);
    assertTrue(supportedStack);

    boolean notSupportedStack = metaInfo.isSupportedStack(NON_EXT_VALUE,
        NON_EXT_VALUE);
    assertFalse(notSupportedStack);
  }

  @Test
  public void isValidService() throws AmbariException {
    boolean valid = metaInfo.isValidService(STACK_NAME_HDP, STACK_VERSION_HDP,
        SERVICE_NAME_HDFS);
    assertTrue(valid);

    boolean invalid = metaInfo.isValidService(STACK_NAME_HDP, STACK_VERSION_HDP, NON_EXT_VALUE);
    assertFalse(invalid);
  }

  @Test
  public void isServiceWithNoConfigs() throws AmbariException {
    assertTrue(metaInfo.isServiceWithNoConfigs(STACK_NAME_HDP, "2.0.8", "SYSTEMML"));
    assertTrue(metaInfo.isServiceWithNoConfigs(STACK_NAME_HDP, "2.0.7", "SYSTEMML"));
    assertFalse(metaInfo.isServiceWithNoConfigs(STACK_NAME_HDP, "2.0.8", "HIVE"));
  }

  @Test
  public void testServiceNameUsingComponentName() throws AmbariException {
    String serviceName = metaInfo.getComponentToService(STACK_NAME_HDP,
        STACK_VERSION_HDP, SERVICE_COMPONENT_NAME);
    assertEquals("HDFS", serviceName);
  }

  /**
   * Method: Map<String, ServiceInfo> getServices(String stackName, String
   * version, String serviceName)
   * @throws AmbariException
   */
  @Test
  public void getServices() throws AmbariException {
    Map<String, ServiceInfo> services = metaInfo.getServices(STACK_NAME_HDP,
        STACK_VERSION_HDP);
    LOG.info("Getting all the services ");
    for (Map.Entry<String, ServiceInfo> entry : services.entrySet()) {
      LOG.info("Service Name " + entry.getKey() + " values " + entry.getValue());
    }
    assertTrue(services.containsKey("HDFS"));
    assertTrue(services.containsKey("MAPREDUCE"));
    assertNotNull(services);
    assertFalse(services.keySet().size() == 0);
  }

  /**
   * Method: getServiceInfo(String stackName, String version, String
   * serviceName)
   */
  @Test
  public void getServiceInfo() throws Exception {
    ServiceInfo si = metaInfo.getService(STACK_NAME_HDP, STACK_VERSION_HDP,
        SERVICE_NAME_HDFS);
    assertNotNull(si);
  }

  @Test
  public void testConfigDependencies() throws Exception {
    ServiceInfo serviceInfo = metaInfo.getService(STACK_NAME_HDP, EXT_STACK_NAME,
        SERVICE_NAME_MAPRED2);
    assertNotNull(serviceInfo);
    assertTrue(!serviceInfo.getConfigDependencies().isEmpty());
  }

  @Test
  public void testGetRepos() throws Exception {
    Map<String, List<RepositoryInfo>> repos = metaInfo.getRepository(
        STACK_NAME_HDP, STACK_VERSION_HDP);
    Set<String> centos5Cnt = new HashSet<>();
    Set<String> centos6Cnt = new HashSet<>();
    Set<String> redhat6cnt = new HashSet<>();
    Set<String> redhat5cnt = new HashSet<>();

    for (List<RepositoryInfo> vals : repos.values()) {
      for (RepositoryInfo repo : vals) {
        LOG.debug("Dumping repo info : {}", repo);
        if (repo.getOsType().equals("centos5")) {
          centos5Cnt.add(repo.getRepoId());
        } else if (repo.getOsType().equals("centos6")) {
          centos6Cnt.add(repo.getRepoId());
        } else if (repo.getOsType().equals("redhat6")) {
          redhat6cnt.add(repo.getRepoId());
        } else if (repo.getOsType().equals("redhat5")) {
          redhat5cnt.add(repo.getRepoId());
        } else {
          fail("Found invalid os " + repo.getOsType());
        }

        if (repo.getRepoId().equals("epel")) {
          assertFalse(repo.getMirrorsList().isEmpty());
          assertNull(repo.getBaseUrl());
        } else {
          assertNull(repo.getMirrorsList());
          assertFalse(repo.getBaseUrl().isEmpty());
        }
      }
    }

    assertEquals(3, centos5Cnt.size());
    assertEquals(3, redhat6cnt.size());
    assertEquals(3, redhat5cnt.size());
    assertEquals(3, centos6Cnt.size());
  }


  /**
   * Make sure global mapping is avaliable when global.xml is
   * in the path.
   */
  @Test
  public void testGlobalMapping() throws Exception {
    ServiceInfo sinfo = metaInfo.getService("HDP",
        "0.2", "HDFS");
    List<PropertyInfo> pinfo = sinfo.getProperties();
    // check all the config knobs and make sure the global one is there
    boolean checkforglobal = false;

    for (PropertyInfo pinfol: pinfo) {
      if ("global.xml".equals(pinfol.getFilename())) {
        checkforglobal = true;
      }
    }
    Assert.assertTrue(checkforglobal);
    sinfo = metaInfo.getService("HDP",
        "0.2", "MAPREDUCE");
    boolean checkforhadoopheapsize = false;
    pinfo = sinfo.getProperties();
    for (PropertyInfo pinfol: pinfo) {
      if ("global.xml".equals(pinfol.getFilename())) {
        if ("hadoop_heapsize".equals(pinfol.getName())) {
          checkforhadoopheapsize = true;
        }
      }
    }
    Assert.assertTrue(checkforhadoopheapsize);
  }

  @Test
  public void testMetaInfoFileFilter() throws Exception {
    String buildDir = tmpFolder.getRoot().getAbsolutePath();
    File stackRoot = new File("src/test/resources/stacks");
    File version = new File("src/test/resources/version");
    if (System.getProperty("os.name").contains("Windows")) {
      stackRoot = new File(ClassLoader.getSystemClassLoader().getResource("stacks").getPath());
      version = new File(new File(ClassLoader.getSystemClassLoader().getResource("").getPath()).getParent(), "version");
    }
    File stackRootTmp = getStackRootTmp(buildDir);
    stackRootTmp.mkdir();
    FileUtils.copyDirectory(stackRoot, stackRootTmp);
    //todo
    //ambariMetaInfo.injector = injector;
    File f1, f2, f3;
    f1 = new File(stackRootTmp.getAbsolutePath() + "/001.svn"); f1.createNewFile();
    f2 = new File(stackRootTmp.getAbsolutePath() + "/abcd.svn/001.svn"); f2.mkdirs(); f2.createNewFile();
    f3 = new File(stackRootTmp.getAbsolutePath() + "/.svn");
    if (!f3.exists()) {
      f3.createNewFile();
    }

    AmbariMetaInfo ambariMetaInfo = createAmbariMetaInfo(stackRootTmp, version, new File(""));

    // Tests the stack is loaded as expected
    getServices();
    getComponentsByService();
    // Check .svn is not part of the stack but abcd.svn is
    Assert.assertNotNull(ambariMetaInfo.getStack("abcd.svn", "001.svn"));

    Assert.assertFalse(ambariMetaInfo.isSupportedStack(".svn", ""));
    Assert.assertFalse(ambariMetaInfo.isSupportedStack(".svn", ""));
  }

  @Test
  public void testGetComponent() throws Exception {
    ComponentInfo component = metaInfo.getComponent(STACK_NAME_HDP,
        STACK_VERSION_HDP, SERVICE_NAME_HDFS, SERVICE_COMPONENT_NAME);
    Assert.assertEquals(component.getName(), SERVICE_COMPONENT_NAME);

    try {
      metaInfo.getComponent(STACK_NAME_HDP,
          STACK_VERSION_HDP, SERVICE_NAME_HDFS, NON_EXT_VALUE);
    } catch (StackAccessException e) {
    }

  }

  @Test
  public void testGetRepositories() throws Exception {
    List<RepositoryInfo> repositories = metaInfo.getRepositories(STACK_NAME_HDP, STACK_VERSION_HDP, OS_TYPE);
    Assert.assertEquals(repositories.size(), REPOS_CNT);
  }

  @Test
  public void testGetRepository() throws Exception {
    RepositoryInfo repository = metaInfo.getRepository(STACK_NAME_HDP, STACK_VERSION_HDP, OS_TYPE, REPO_ID);
    Assert.assertEquals(repository.getRepoId(), REPO_ID);

    try {
      metaInfo.getRepository(STACK_NAME_HDP, STACK_VERSION_HDP, OS_TYPE, NON_EXT_VALUE);
    } catch (StackAccessException e) {
    }
  }

  @Test
  public void testGetService() throws Exception {
    ServiceInfo service = metaInfo.getService(STACK_NAME_HDP, STACK_VERSION_HDP, SERVICE_NAME_HDFS);
    Assert.assertEquals(service.getName(), SERVICE_NAME_HDFS);
    try {
      metaInfo.getService(STACK_NAME_HDP, STACK_VERSION_HDP, NON_EXT_VALUE);
    } catch (StackAccessException e) {
    }

  }

  @Test
  public void testGetStacks() {
    //Collection<StackInfo> stacks = metaInfo.getStacks();
    //todo: complete test
  }

  @Test
  public void testGetStackInfo() throws Exception {
    StackInfo stackInfo = metaInfo.getStack(STACK_NAME_HDP, STACK_VERSION_HDP);
    Assert.assertEquals(stackInfo.getName(), STACK_NAME_HDP);
    Assert.assertEquals(stackInfo.getVersion(), STACK_VERSION_HDP);
    try {
      metaInfo.getStack(STACK_NAME_HDP, NON_EXT_VALUE);
    } catch (StackAccessException e) {
    }
  }

  @Test
  public void testGetStackParentVersions() throws Exception {
    List<String> parents = metaInfo.getStackParentVersions(STACK_NAME_HDP, "2.0.8");
    Assert.assertEquals(3, parents.size());
    Assert.assertEquals("2.0.7", parents.get(0));
    Assert.assertEquals("2.0.6", parents.get(1));
    Assert.assertEquals("2.0.5", parents.get(2));
  }

  @Test
  public void testGetProperties() throws Exception {
    Set<PropertyInfo> properties = metaInfo.getServiceProperties(STACK_NAME_HDP, STACK_VERSION_HDP, SERVICE_NAME_HDFS);
    Assert.assertEquals(properties.size(), PROPERTIES_CNT);
  }

  @Test
  public void testGetPropertiesNoName() throws Exception {
    Set<PropertyInfo> properties = metaInfo.getPropertiesByName(STACK_NAME_HDP, STACK_VERSION_HDP, SERVICE_NAME_HDFS, PROPERTY_NAME);
    Assert.assertEquals(1, properties.size());
    for (PropertyInfo propertyInfo : properties) {
      Assert.assertEquals(PROPERTY_NAME, propertyInfo.getName());
      Assert.assertEquals(FILE_NAME, propertyInfo.getFilename());
    }

    try {
      metaInfo.getPropertiesByName(STACK_NAME_HDP, STACK_VERSION_HDP, SERVICE_NAME_HDFS, NON_EXT_VALUE);
    } catch (StackAccessException e) {
    }

  }

  @Test
  public void testGetPropertiesSharedName() throws Exception {
    Set<PropertyInfo> properties = metaInfo.getPropertiesByName(STACK_NAME_HDP, STACK_VERSION_HDP_02, SERVICE_NAME_HDFS, SHARED_PROPERTY_NAME);
    Assert.assertEquals(2, properties.size());
    for (PropertyInfo propertyInfo : properties) {
      Assert.assertEquals(SHARED_PROPERTY_NAME, propertyInfo.getName());
      Assert.assertTrue(propertyInfo.getFilename().equals(HADOOP_ENV_FILE_NAME)
        || propertyInfo.getFilename().equals(HDFS_LOG4J_FILE_NAME));
    }
  }

  @Test
  public void testGetOperatingSystems() throws Exception {
    Set<OperatingSystemInfo> operatingSystems = metaInfo.getOperatingSystems(STACK_NAME_HDP, STACK_VERSION_HDP);
    Assert.assertEquals(OS_CNT, operatingSystems.size());
  }

  @Test
  public void testGetOperatingSystem() throws Exception {
    OperatingSystemInfo operatingSystem = metaInfo.getOperatingSystem(STACK_NAME_HDP, STACK_VERSION_HDP, OS_TYPE);
    Assert.assertEquals(operatingSystem.getOsType(), OS_TYPE);


    try {
      metaInfo.getOperatingSystem(STACK_NAME_HDP, STACK_VERSION_HDP, NON_EXT_VALUE);
    } catch (StackAccessException e) {
    }
  }

  @Test
  public void isOsSupported() throws Exception {
    Assert.assertTrue(metaInfo.isOsSupported("redhat5"));
    Assert.assertTrue(metaInfo.isOsSupported("centos5"));
    Assert.assertTrue(metaInfo.isOsSupported("oraclelinux5"));
    Assert.assertTrue(metaInfo.isOsSupported("redhat6"));
    Assert.assertTrue(metaInfo.isOsSupported("centos6"));
    Assert.assertTrue(metaInfo.isOsSupported("oraclelinux6"));
    Assert.assertTrue(metaInfo.isOsSupported("suse11"));
    Assert.assertTrue(metaInfo.isOsSupported("sles11"));
    Assert.assertTrue(metaInfo.isOsSupported("ubuntu12"));
    Assert.assertTrue(metaInfo.isOsSupported("win2008server6"));
    Assert.assertTrue(metaInfo.isOsSupported("win2008serverr26"));
    Assert.assertTrue(metaInfo.isOsSupported("win2012server6"));
    Assert.assertTrue(metaInfo.isOsSupported("win2012serverr26"));
  }

  @Test
  public void testExtendedStackDefinition() throws Exception {
    StackInfo stackInfo = metaInfo.getStack(STACK_NAME_HDP, EXT_STACK_NAME);
    Assert.assertTrue(stackInfo != null);
    Collection<ServiceInfo> serviceInfos = stackInfo.getServices();
    Assert.assertFalse(serviceInfos.isEmpty());
    Assert.assertTrue(serviceInfos.size() > 1);
    ServiceInfo deletedService = null;
    ServiceInfo redefinedService = null;
    for (ServiceInfo serviceInfo : serviceInfos) {
      if (serviceInfo.getName().equals("SQOOP")) {
        deletedService = serviceInfo;
      }
      if (serviceInfo.getName().equals("YARN")) {
        redefinedService = serviceInfo;
      }
    }
    Assert.assertNull("SQOOP is a deleted service, should not be a part of " +
      "the extended stack.", deletedService);
    Assert.assertNotNull(redefinedService);
    // Components
    Assert.assertEquals("YARN service is expected to be defined with 4 active" +
      " components.", 4, redefinedService.getComponents().size());
    Assert.assertEquals("TEZ is expected to be a part of extended stack " +
      "definition", "TEZ", redefinedService.getClientComponent().getName());
    Assert.assertFalse("YARN CLIENT is a deleted component.",
      redefinedService.getClientComponent().getName().equals("YARN_CLIENT"));
    // Properties
    Assert.assertNotNull(redefinedService.getProperties());
    Assert.assertTrue(redefinedService.getProperties().size() > 4);
    PropertyInfo deleteProperty1 = null;
    PropertyInfo deleteProperty2 = null;
    PropertyInfo redefinedProperty1 = null;
    PropertyInfo redefinedProperty2 = null;
    PropertyInfo redefinedProperty3 = null;
    PropertyInfo inheritedProperty = null;
    PropertyInfo newProperty = null;
    PropertyInfo newEnhancedProperty = null;
    PropertyInfo propertyWithExtraValueAttributes = null;
    PropertyInfo originalProperty = null;

    PropertyDependencyInfo propertyDependencyInfo =
      new PropertyDependencyInfo("yarn-site", "new-enhanced-yarn-property");

    for (PropertyInfo propertyInfo : redefinedService.getProperties()) {
      if (propertyInfo.getName().equals("yarn.resourcemanager.resource-tracker.address")) {
        deleteProperty1 = propertyInfo;
      } else if (propertyInfo.getName().equals("yarn.resourcemanager.scheduler.address")) {
        deleteProperty2 = propertyInfo;
      } else if (propertyInfo.getName().equals("yarn.resourcemanager.address")) {
        redefinedProperty1 = propertyInfo;
      } else if (propertyInfo.getName().equals("yarn.resourcemanager.admin.address")) {
        redefinedProperty2 = propertyInfo;
      } else if (propertyInfo.getName().equals("yarn.nodemanager.health-checker.interval-ms")) {
        redefinedProperty3 = propertyInfo;
      } else if (propertyInfo.getName().equals("yarn.nodemanager.address")) {
        inheritedProperty = propertyInfo;
      } else if (propertyInfo.getName().equals("new-yarn-property")) {
        newProperty = propertyInfo;
      } else if (propertyInfo.getName().equals("new-enhanced-yarn-property")) {
        newEnhancedProperty = propertyInfo;
      } else if (propertyInfo.getName().equals("yarn.nodemanager.aux-services")) {
        originalProperty = propertyInfo;
      } else if (propertyInfo.getName().equals("property.with.extra.value.attributes")) {
        propertyWithExtraValueAttributes = propertyInfo;
      }
    }

    Assert.assertNull(deleteProperty1);
    Assert.assertNull(deleteProperty2);
    Assert.assertNotNull(redefinedProperty1);
    Assert.assertNotNull(redefinedProperty2);
    Assert.assertNotNull("yarn.nodemanager.address expected to be inherited " +
      "from parent", inheritedProperty);
    Assert.assertEquals("localhost:100009", redefinedProperty1.getValue());
    // Parent property value will result in property being present in the child stack
    Assert.assertNotNull(redefinedProperty3);
    Assert.assertEquals("135000", redefinedProperty3.getValue());
    // Child can override parent property to empty value
    Assert.assertEquals("", redefinedProperty2.getValue());
    // New property
    Assert.assertNotNull(newProperty);
    Assert.assertEquals("some-value", newProperty.getValue());
    Assert.assertEquals("some description.", newProperty.getDescription());
    Assert.assertEquals("yarn-site.xml", newProperty.getFilename());
    Assert.assertEquals(1, newProperty.getDependedByProperties().size());
    Assert.assertTrue(newProperty.getDependedByProperties().contains(propertyDependencyInfo));
    // New enhanced property
    Assert.assertNotNull(newEnhancedProperty);
    Assert.assertEquals("1024", newEnhancedProperty.getValue());
    Assert.assertEquals("some enhanced description.", newEnhancedProperty.getDescription());
    Assert.assertEquals("yarn-site.xml", newEnhancedProperty.getFilename());
    Assert.assertEquals(2, newEnhancedProperty.getDependsOnProperties().size());
    Assert.assertTrue(newEnhancedProperty.getDependsOnProperties().contains(new PropertyDependencyInfo("yarn-site", "new-yarn-property")));
    Assert.assertTrue(newEnhancedProperty.getDependsOnProperties().contains(new PropertyDependencyInfo("global", "yarn_heapsize")));
    Assert.assertEquals("MB", newEnhancedProperty.getPropertyValueAttributes().getUnit());
    Assert.assertEquals("int", newEnhancedProperty.getPropertyValueAttributes().getType());
    Assert.assertEquals("512", newEnhancedProperty.getPropertyValueAttributes().getMinimum());
    Assert.assertEquals("15360", newEnhancedProperty.getPropertyValueAttributes().getMaximum());
    Assert.assertEquals("256", newEnhancedProperty.getPropertyValueAttributes().getIncrementStep());
    Assert.assertNull(newEnhancedProperty.getPropertyValueAttributes().getEntries());
    Assert.assertNull(newEnhancedProperty.getPropertyValueAttributes().getEntriesEditable());

    // property with extra value attributes
    Assert.assertTrue(propertyWithExtraValueAttributes.getPropertyValueAttributes().getEmptyValueValid());
    Assert.assertTrue(propertyWithExtraValueAttributes.getPropertyValueAttributes().getVisible());
    Assert.assertTrue(propertyWithExtraValueAttributes.getPropertyValueAttributes().getReadOnly());
    Assert.assertEquals(Boolean.FALSE, propertyWithExtraValueAttributes.getPropertyValueAttributes().getEditableOnlyAtInstall());
    Assert.assertEquals(Boolean.FALSE, propertyWithExtraValueAttributes.getPropertyValueAttributes().getOverridable());
    Assert.assertEquals(Boolean.FALSE, propertyWithExtraValueAttributes.getPropertyValueAttributes().getShowPropertyName());

    // Original property
    Assert.assertNotNull(originalProperty);
    Assert.assertEquals("mapreduce.shuffle", originalProperty.getValue());
    Assert.assertEquals("Auxilliary services of NodeManager",
      originalProperty.getDescription());
    Assert.assertEquals(6, redefinedService.getConfigDependencies().size());
    Assert.assertEquals(7, redefinedService.getConfigDependenciesWithComponents().size());
  }

  @Test
  public void testPropertyCount() throws Exception {
    Set<PropertyInfo> properties = metaInfo.getServiceProperties(STACK_NAME_HDP, STACK_VERSION_HDP_02, SERVICE_NAME_HDFS);
    // 3 empty properties
    Assert.assertEquals(103, properties.size());
  }

  @Test
  public void testBadStack() throws Exception {
    File stackRoot = new File("src/test/resources/bad-stacks");
    File version = new File("src/test/resources/version");
    if (System.getProperty("os.name").contains("Windows")) {
      stackRoot = new File(ClassLoader.getSystemClassLoader().getResource("bad-stacks").getPath());
      version = new File(new File(ClassLoader.getSystemClassLoader().getResource("").getPath()).getParent(), "version");
    }
    LOG.info("Stacks file " + stackRoot.getAbsolutePath());


    TestAmbariMetaInfo ambariMetaInfo = createAmbariMetaInfo(stackRoot, version, new File(""));
    Assert.assertEquals(1, ambariMetaInfo.getStackManager().getStacks().size());
    Assert.assertEquals(false, ambariMetaInfo.getStackManager().getStack("HDP", "0.1").isValid());
    Assert.assertEquals(2, ambariMetaInfo.getStackManager().getStack("HDP", "0.1").getErrors().size());

  }

  @Test
  public void testMetricsJson() throws Exception {
    ServiceInfo svc = metaInfo.getService(STACK_NAME_HDP, "2.0.5", "HDFS");
    Assert.assertNotNull(svc);
    Assert.assertNotNull(svc.getMetricsFile());

    svc = metaInfo.getService(STACK_NAME_HDP, "2.0.6", "HDFS");
    Assert.assertNotNull(svc);
    Assert.assertNotNull(svc.getMetricsFile());

    List<MetricDefinition> list = metaInfo.getMetrics(STACK_NAME_HDP, "2.0.5", "HDFS", SERVICE_COMPONENT_NAME, Resource.Type.Component.name());
    Assert.assertNotNull(list);
    checkNoAggregatedFunctionsForJmx(list);

    list = metaInfo.getMetrics(STACK_NAME_HDP, "2.0.5", "HDFS", "DATANODE", Resource.Type.Component.name());
    Assert.assertNull(list);

    List<MetricDefinition> list0 = metaInfo.getMetrics(STACK_NAME_HDP, "2.0.5", "HDFS", "DATANODE", Resource.Type.Component.name());
    Assert.assertNull(list0);
    Assert.assertTrue("Expecting subsequent calls to use a cached value for the definition", list == list0);

    // not explicitly defined, uses 2.0.5
    list = metaInfo.getMetrics(STACK_NAME_HDP, "2.0.6", "HDFS", "DATANODE", Resource.Type.Component.name());
    Assert.assertNull(list);
  }

  @Test
  public void testKerberosJson() throws Exception {
    ServiceInfo svc;

    svc = metaInfo.getService(STACK_NAME_HDP, "2.0.8", "HDFS");
    Assert.assertNotNull(svc);

    File kerberosDescriptorFile1 = svc.getKerberosDescriptorFile();
    Assert.assertNotNull(kerberosDescriptorFile1);
