@Test
public void synchronizationAllVersions() throws Throwable {

    // toggle on all-versions synchronization
    TestXenonConfiguration.override(
            SynchronizationTaskService.class,
            "SYNCH_ALL_VERSIONS",
            "true"
    );
    this.isPeerSynchronizationEnabled = false;
    setUp(this.nodeCount);
    this.host.joinNodesAndVerifyConvergence(this.nodeCount);
    // relax quorum, so we can perform updates after a host is stopped
    this.host.setNodeGroupQuorum(this.nodeCount - 1);
    // expire node that went away quickly to avoid a lot of log spam from gossip failures
    NodeGroupConfig cfg = new NodeGroupConfig();
    cfg.nodeRemovalDelayMicros = TimeUnit.SECONDS.toMicros(1);
    this.host.setNodeGroupConfig(cfg);
    // stop one of the hosts, preserve its index
    VerificationHost stoppedHost = this.host.getPeerHost();
    this.host.stopHostAndPreserveState(stoppedHost);
    // wait for stopped host to be removed from node group
    this.host.waitForNodeGroupConvergence(this.nodeCount - 1, this.nodeCount - 1);
    // Create some services
    VerificationHost peerHost = this.host.getPeerHost();
    URI peerHostUri = peerHost.getUri();
    Map<String, ExampleServiceState> exampleStatesPerSelfLink = createExampleServices(
            peerHostUri);
    // update services to create some version history
    doExampleServicePatch(exampleStatesPerSelfLink, peerHostUri);
    // increase quorum on existing nodes, so they wait for restarted host to join
    this.host.setNodeGroupQuorum(this.nodeCount);
    // restart stopped host
    stoppedHost.setPort(0);
    stoppedHost.setSecurePort(0);
    stoppedHost.setPeerSynchronizationEnabled(false);
    assertTrue(VerificationHost.restartStatefulHost(stoppedHost, false));
    // join restarted host and wait for node group convergence
    this.host.addPeerNode(stoppedHost);
    this.host.joinNodesAndVerifyConvergence(this.nodeCount);
    // set quorum on new node as well
    this.host.setNodeGroupQuorum(this.nodeCount);
    this.host.waitForNodeGroupConvergence();
    // find example factory owner
    VerificationHost owner = this.host.getOwnerPeer(ExampleService.FACTORY_LINK);
    // wait for node selector availability before manually trigger a synchronization task
    this.host.waitFor("wait node selector available timeout", () -> {
        Operation op = Operation.createGet(owner, ServiceUriPaths.DEFAULT_NODE_SELECTOR);
        NodeSelectorState nss = this.host.getTestRequestSender().sendAndWait(op, NodeSelectorState.class);
        if (nss.status == NodeSelectorState.Status.AVAILABLE) {
            return true;
        }
        return false;
    });
    // kick-off factory synchronization and wait for it to finish
    startSynchronizationTaskAndWait(owner,
            ExampleService.FACTORY_LINK, ExampleServiceState.class, 1L);
    this.host.waitForReplicatedFactoryChildServiceConvergence(
            getFactoriesPerNodeGroup(ExampleService.FACTORY_LINK),
            exampleStatesPerSelfLink,
            this.exampleStateConvergenceChecker, exampleStatesPerSelfLink.size(),
            this.updateCount, this.replicationFactor);
    verifySameVersionsAcrossHosts(true);
}