content: /*
   * Copyright 1999-2018 Alibaba Group Holding Ltd.
   *
   * 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 com.alibaba.csp.sentinel.node;
  
  import org.junit.Test;
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.Set;
  import java.util.concurrent.Callable;
  import java.util.concurrent.ConcurrentHashMap;
  import java.util.concurrent.CountDownLatch;
  import java.util.concurrent.ExecutorService;
  import java.util.concurrent.Executors;
  
  import static org.junit.Assert.*;
  
  /**
   * Test cases for {@link ClusterNode}.
   *
   * @author cdfive
   */
  public class ClusterNodeTest {
  
      @Test
      public void testGetOrCreateOriginNodeSingleThread() {
          ClusterNode clusterNode = new ClusterNode("test");
  
          String origin1 = "origin1";
          Node originNode1 = clusterNode.getOrCreateOriginNode(origin1);
          assertNotNull(originNode1);
          assertEquals(1, clusterNode.getOriginCountMap().size());
  
          String origin2 = "origin2";
          Node originNode2 = clusterNode.getOrCreateOriginNode(origin2);
          assertNotNull(originNode2);
          assertEquals(2, clusterNode.getOriginCountMap().size());
          assertNotSame(originNode1, originNode2);
  
          // test same origin, no StatisticNode added into the originCountMap
          Node tmpOriginNode = clusterNode.getOrCreateOriginNode(origin1);
          assertEquals(2, clusterNode.getOriginCountMap().size());
          assertSame(tmpOriginNode, originNode1);
  
          assertTrue(clusterNode.getOriginCountMap().containsKey(origin1));
          assertTrue(clusterNode.getOriginCountMap().containsKey(origin2));
      }
  
      @Test
      public void testGetOrCreateOriginNodeMultiThread() {
          // Note: in JUnit 4, repeat execute a test method is not very convenient
          // for simple, here use a loop instead
          // https://stackoverflow.com/questions/1492856/easy-way-of-running-the-same-junit-test-over-and-over
          // in JUnit 5, use @RepeatedTest(10)
  
          // execute 10 times, test will have chance to failed, if remove the lock in ClusterNode
          final int testTimes = 10;
  
          for (int times = 0; times < testTimes; times++) {
              final ClusterNode clusterNode = new ClusterNode("test");
  
              // Store all distinct nodes by calling ClusterNode#getOrCreateOriginNode.
              // Here we need a thread-safe concurrent set (created from ConcurrentHashMap).
              final Set<Node> createdNodes = Collections.newSetFromMap(new ConcurrentHashMap<Node, Boolean>());
  
              // 10 threads, 3 origins, 20 tasks (calling 20 times of ClusterNode#getOrCreateOriginNode concurrently)
              final ExecutorService es = Executors.newFixedThreadPool(10);
              final List<String> origins = Arrays.asList("origin1", "origin2", "origin3");
              int taskCount = 20;
              final CountDownLatch latch = new CountDownLatch(taskCount);
  
              List<Callable<Object>> tasks = new ArrayList<Callable<Object>>(taskCount);
              for (int i = 0; i < taskCount; i++) {
                  final int index = i % origins.size();
                  tasks.add(new Callable<Object>() {
                      @Override
                      public Object call() throws Exception {
                          // one task call one times of ClusterNode#getOrCreateOriginNode
                          Node node = clusterNode.getOrCreateOriginNode(origins.get(index));
                          // add the result node to the createdNodes set
                          createdNodes.add(node);
                          latch.countDown();
  
                          return null;
                      }
                  });
              }
  
              try {
                  es.invokeAll(tasks);
                  latch.await();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              es.shutdown();
  
              // origins.size() origins, the same count as the originCountMap
              assertEquals(origins.size(), clusterNode.getOriginCountMap().size());
  
              // not use `assertEquals(origins.size(), createdNodes.size());`, but a compare judgement for debug
              if (origins.size() != createdNodes.size()) {
                  // for debug, we can add a breakpoint here to see the detail info of createdNodes,
                  // if we removed the lock in ClusterNode
                  fail("originCountMap's size should " + origins.size() + ", but actual " + createdNodes.size());
              }
  
              // verify originCountMap's key
              for (String origin : origins) {
                  assertTrue(clusterNode.getOriginCountMap().containsKey(origin));
              }
          }
      }
  }
  