/*
 * Copyright 2017-present Open Networking Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.atomix.core;

import io.atomix.cluster.Member;
import io.atomix.cluster.MemberId;
import io.atomix.primitive.protocol.PrimitiveProtocol;
import io.atomix.protocols.backup.partition.PrimaryBackupPartitionGroup;
import io.atomix.protocols.raft.partition.RaftPartitionGroup;
import org.junit.AfterClass;
import org.junit.BeforeClass;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Base Atomix test.
 */
public abstract class AbstractPrimitiveTest extends AbstractAtomixTest {
  private static List<Atomix> instances;
  private static int id = 10;

  /**
   * Returns the primitive protocol with which to test.
   *
   * @return the protocol with which to test
   */
  protected abstract PrimitiveProtocol protocol();

  /**
   * Returns a new Atomix instance.
   *
   * @return a new Atomix instance.
   */
  protected Atomix atomix() throws Exception {
    Atomix instance = createAtomix(Member.Type.EPHEMERAL, id++, Arrays.asList(1, 2, 3), Arrays.asList());
    instance.start().get(10, TimeUnit.SECONDS);
    instances.add(instance);
    return instance;
  }

  @BeforeClass
  public static void setupAtomix() throws Exception {
    AbstractAtomixTest.setupAtomix();
    Function<Atomix.Builder, Atomix> build = builder ->
        builder.withManagementGroup(RaftPartitionGroup.builder("system")
            .withNumPartitions(1)
            .withMembers("1", "2", "3")
            .build())
            .addPartitionGroup(RaftPartitionGroup.builder("raft")
                .withNumPartitions(3)
                .withMembers("1", "2", "3")
                .build())
            .addPartitionGroup(PrimaryBackupPartitionGroup.builder("data")
                .withNumPartitions(7)
                .build())
            .build();
    instances = new ArrayList<>();
    instances.add(createAtomix(Member.Type.PERSISTENT, 1, Arrays.asList(1, 2, 3), Arrays.asList(), build));
    instances.add(createAtomix(Member.Type.PERSISTENT, 2, Arrays.asList(1, 2, 3), Arrays.asList(), build));
    instances.add(createAtomix(Member.Type.PERSISTENT, 3, Arrays.asList(1, 2, 3), Arrays.asList(), build));
    List<CompletableFuture<Atomix>> futures = instances.stream().map(a -> a.start().thenApply(v -> a)).collect(Collectors.toList());
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).get(30, TimeUnit.SECONDS);
  }

  @AfterClass
  public static void teardownAtomix() throws Exception {
    List<CompletableFuture<Void>> futures = instances.stream().map(Atomix::stop).collect(Collectors.toList());
    try {
      CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).join();
    } catch (Exception e) {
      // Do nothing
    }
    AbstractAtomixTest.teardownAtomix();
  }
}

@@ -669,43 +669,8 @@
 er;%0A
-import io.atomix.cluster.MemberId;%0A
 impo
@@ -851,24 +851,48 @@
 itionGroup;%0A
+import org.junit.After;%0A
 import org.j
@@ -908,16 +908,41 @@
 rClass;%0A
+import org.junit.Before;%0A
 import o
@@ -1330,24 +1330,61 @@
 Atomix%3E 
-instance
+servers;%0A  private static List%3CAtomix%3E client
 s;%0A  pri
@@ -1817,24 +1817,51 @@
 .asList());%0A
+    clients.add(instance);%0A
     instance
@@ -1873,17 +1873,17 @@
 t().get(
-1
+3
 0, TimeU
@@ -1900,37 +1900,8 @@
 S);%0A
-    instances.add(instance);%0A
     
@@ -1963,22 +1963,23 @@
 id setup
-Atomix
+Cluster
 () throw
@@ -2594,24 +2594,22 @@
 ();%0A    
-instance
+server
 s = new 
@@ -2623,32 +2623,30 @@
 st%3C%3E();%0A    
-instance
+server
 s.add(create
@@ -2729,32 +2729,30 @@
 uild));%0A    
-instance
+server
 s.add(create
@@ -2839,24 +2839,22 @@
 ));%0A    
-instance
+server
 s.add(cr
@@ -2983,32 +2983,30 @@
 %3E futures = 
-instance
+server
 s.stream().m
@@ -3231,22 +3231,23 @@
 teardown
-Atomix
+Cluster
 () throw
@@ -3304,24 +3304,22 @@
 tures = 
-instance
+server
 s.stream
@@ -3571,11 +3571,446 @@
 x();%0A  %7D
+%0A%0A  @Before%0A  public void setupTest() throws Exception %7B%0A    clients = new ArrayList%3C%3E();%0A    id = 10;%0A  %7D%0A%0A  @After%0A  public void teardownTest() throws Exception %7B%0A    List%3CCompletableFuture%3CVoid%3E%3E futures = clients.stream().map(Atomix::stop).collect(Collectors.toList());%0A    try %7B%0A      CompletableFuture.allOf(futures.toArray(new CompletableFuture%5Bfutures.size()%5D)).join();%0A    %7D catch (Exception e) %7B%0A      // Do nothing%0A    %7D%0A  %7D
 %0A%7D%0A

Use a single client instance per primitive test to avoid pollution of cluster membership across tests.