@Test
  public void testTransitionHostVersionAdvanced() throws Exception {
    String clusterName = "c1";
    String v1 = "2.2.0-123";
    StackId stackId = new StackId("HDP-2.2.0");

    RepositoryVersionEntity rv1 = helper.getOrCreateRepositoryVersion(stackId, v1);

    Map<String, String> hostAttributes = new HashMap<>();
    hostAttributes.put("os_family", "redhat");
    hostAttributes.put("os_release_version", "6.4");

    Cluster cluster = createClusterForRU(clusterName, rv1, hostAttributes);

    // Begin install by starting to advertise versions
    // Set the version for the HostComponentState objects
    int versionedComponentCount = 0;
    List<HostComponentStateEntity> hostComponentStates = hostComponentStateDAO.findAll();
    for(int i = 0; i < hostComponentStates.size(); i++) {
      HostComponentStateEntity hce = hostComponentStates.get(i);
      ComponentInfo compInfo = metaInfo.getComponent(
          stackId.getStackName(), stackId.getStackVersion(),
          hce.getServiceName(),
          hce.getComponentName());

      if (compInfo.isVersionAdvertised()) {
        hce.setVersion(v1);
        hostComponentStateDAO.merge(hce);
        versionedComponentCount++;
      }

      // Simulate the StackVersionListener during the installation of the first Stack Version
      Service svc = cluster.getService(hce.getServiceName());
      ServiceComponent svcComp = svc.getServiceComponent(hce.getComponentName());
      ServiceComponentHost scHost = svcComp.getServiceComponentHost(hce.getHostName());

      scHost.recalculateHostVersionState();

      if (versionedComponentCount > 0) {
        // On the first component with a version, a RepoVersion should have been created
        RepositoryVersionEntity repositoryVersion = repositoryVersionDAO.findByStackAndVersion(stackId, v1);
        Assert.assertNotNull(repositoryVersion);
      }
    }

    // Add another Host with components ZK Server, ZK Client, and Ganglia Monitor.
    // This host should get a HostVersion in CURRENT, and the ClusterVersion should stay in CURRENT
    addHost("h-4", hostAttributes);
    clusters.mapHostToCluster("h-4", clusterName);

    Service svc2 = cluster.getService("ZOOKEEPER");
    Service svc3 = cluster.getService("GANGLIA");

    ServiceComponent sc2CompA = svc2.getServiceComponent("ZOOKEEPER_SERVER");
    ServiceComponent sc2CompB = svc2.getServiceComponent("ZOOKEEPER_CLIENT");
    ServiceComponent sc3CompB = svc3.getServiceComponent("GANGLIA_MONITOR");

    ServiceComponentHost schHost4Serv2CompA = serviceComponentHostFactory.createNew(sc2CompA, "h-4");
    ServiceComponentHost schHost4Serv2CompB = serviceComponentHostFactory.createNew(sc2CompB, "h-4");
    ServiceComponentHost schHost4Serv3CompB = serviceComponentHostFactory.createNew(sc3CompB, "h-4");
    sc2CompA.addServiceComponentHost(schHost4Serv2CompA);
    sc2CompB.addServiceComponentHost(schHost4Serv2CompB);
    sc3CompB.addServiceComponentHost(schHost4Serv3CompB);

    simulateStackVersionListener(stackId, v1, cluster, hostComponentStateDAO.findByHost("h-4"));

    Collection<HostVersionEntity> hostVersions = hostVersionDAO.findAll();

    // h-4 doesn't have a host version record yet
    Assert.assertEquals(hostVersions.size(), clusters.getHosts().size());

    HostVersionEntity h4Version1 = hostVersionDAO.findByClusterStackVersionAndHost(clusterName, stackId, v1, "h-4");
    Assert.assertNotNull(h4Version1);
    Assert.assertEquals(h4Version1.getState(), RepositoryVersionState.CURRENT);

    // Distribute bits for a new repo
    String v2 = "2.2.0-456";
    RepositoryVersionEntity rv2 = helper.getOrCreateRepositoryVersion(stackId, v2);
    for(String hostName : clusters.getHostsForCluster(clusterName).keySet()) {
      HostEntity host = hostDAO.findByName(hostName);
      HostVersionEntity hve = new HostVersionEntity(host, rv2, RepositoryVersionState.INSTALLED);
      hostVersionDAO.create(hve);
    }

    // Add one more Host, with only Ganglia on it. It should have a HostVersion in NOT_REQUIRED for v2,
    // as Ganglia isn't versionable
    Host host5 = addHost("h-5", hostAttributes);
    clusters.mapAndPublishHostsToCluster(Collections.singleton("h-5"), clusterName);

    // verify that the new host version was added for the existing repo
    HostVersionEntity h5Version1 = hostVersionDAO.findHostVersionByHostAndRepository(host5.getHostEntity(), rv1);
    HostVersionEntity h5Version2 = hostVersionDAO.findHostVersionByHostAndRepository(host5.getHostEntity(), rv2);

    Assert.assertEquals(RepositoryVersionState.NOT_REQUIRED, h5Version1.getState());
    Assert.assertEquals(RepositoryVersionState.NOT_REQUIRED, h5Version2.getState());

    ServiceComponentHost schHost5Serv3CompB = serviceComponentHostFactory.createNew(sc3CompB, "h-5");
    sc3CompB.addServiceComponentHost(schHost5Serv3CompB);

    // Host 5 will be in OUT_OF_SYNC, so redistribute bits to it so that it reaches a state of INSTALLED
    h5Version2 = hostVersionDAO.findByClusterStackVersionAndHost(clusterName, stackId, v2, "h-5");
    Assert.assertNotNull(h5Version2);
    Assert.assertEquals(RepositoryVersionState.NOT_REQUIRED, h5Version2.getState());

    h5Version2.setState(RepositoryVersionState.INSTALLED);
    hostVersionDAO.merge(h5Version2);

    // Perform an RU.
    // Verify that on first component with the new version, the ClusterVersion transitions to UPGRADING.
    // For hosts with only components that advertise a version, they HostVersion should be in UPGRADING.
    // For the remaining hosts, the HostVersion should stay in INSTALLED.
    versionedComponentCount = 0;
    hostComponentStates = hostComponentStateDAO.findAll();
    for(int i = 0; i < hostComponentStates.size(); i++) {
      HostComponentStateEntity hce = hostComponentStates.get(i);
      ComponentInfo compInfo = metaInfo.getComponent(
          stackId.getStackName(), stackId.getStackVersion(),
          hce.getServiceName(),
          hce.getComponentName());

      if (compInfo.isVersionAdvertised()) {
        hce.setVersion(v2);
        hostComponentStateDAO.merge(hce);
        versionedComponentCount++;
      }

      // Simulate the StackVersionListener during the installation of the first Stack Version
      Service svc = cluster.getService(hce.getServiceName());
      ServiceComponent svcComp = svc.getServiceComponent(hce.getComponentName());
      ServiceComponentHost scHost = svcComp.getServiceComponentHost(hce.getHostName());

      scHost.recalculateHostVersionState();

      if (versionedComponentCount > 0) {
        // On the first component with a version, a RepoVersion should have been created
        RepositoryVersionEntity repositoryVersion = repositoryVersionDAO.findByStackAndVersion(stackId, v2);
        Assert.assertNotNull(repositoryVersion);
      }
    }

    Collection<HostVersionEntity> v2HostVersions = hostVersionDAO.findByClusterStackAndVersion(clusterName, stackId, v2);
    Assert.assertEquals(v2HostVersions.size(), clusters.getHostsForCluster(clusterName).size());
    for (HostVersionEntity hve : v2HostVersions) {
      Assert.assertTrue(TERMINAL_VERSION_STATES.contains(hve.getState()));
    }
  }
Comprehensive test for host versions. It creates a cluster with 3 hosts and
3 services, one of which does not advertise a version. It then verifies
that all 3 hosts have a version of CURRENT, and so does the cluster. It
then adds one more host with a component, so its HostVersion will
initialize in CURRENT. Next, it distributes a repo so that it is INSTALLED
on the 4 hosts. It then adds one more host, whose HostVersion will be
OUT_OF_SYNC for the new repo. After redistributing bits again, it simulates
an RU. Finally, some of the hosts will end up with a HostVersion in
UPGRADED, and others still in INSTALLED.

@throws Exception
ambari-server/src/test/java/org/apache/ambari/server/state/cluster/ClusterTest.java