/**
   * Generates {@link #countToGenerate} ids, and insures that they are all unique.
   *
   * @throws Exception
   */
  @Test
  public void testGenerateUniqueIdsSingleThread() throws Exception {
    long start = Calendar.getInstance().getTimeInMillis();
    for (int i = 0; i < countToGenerate; i++) {
      // Select an ID type to generate, based on a randomized seed.
      double seed = Math.random();
      log.trace("Seed is {}", seed);
      Types t = selectType(seed, Types.values());
      log.trace("Selected type {} with seed value {}", t, seed);

      // If debugging is enabled, keep track of the number of ids created for each type.
      if (log.isDebugEnabled()) {
        if (idTypeDistribution.containsKey(t)) {
          idTypeDistribution.get(t).getAndAdd(1);
        } else {
          idTypeDistribution.put(t, new AtomicInteger(1));
        }
      }

      // Create an ID, and keep it in a Set.
      generatedIds.add(reqFactory.createIdApiRequest(t).execute(hc));
    }

    if (log.isDebugEnabled()) {
      StringBuilder sb = new StringBuilder("ID distribution:\n");
      int totalGenerated = 0;
      for (Types t : Types.values()) {
        final Integer typeTotal = idTypeDistribution.get(t).get();
        totalGenerated += typeTotal;
        sb.append("Type: ").append(t).append(" Count: ").append(typeTotal).append("\n");
      }
      sb.append("Total generated: ").append(totalGenerated).append("\n");
      sb.append("Unique generated: ").append(generatedIds.size()).append("\n");
      sb.append("Execution time: ")
          .append(Calendar.getInstance().getTimeInMillis() - start)
          .append(" ms\n");
      log.debug(sb.toString());
    }

    // The number of generated IDs (stored in the Set) should equal 'countToGenerate'
    assertEquals(
        "Expected "
            + countToGenerate
            + " to be generated, but the Set contained "
            + generatedIds.size()
            + ".  Some ids may not have been unique.",
        countToGenerate,
        generatedIds.size());
  }
  /**
   * Generates {@link #countToGenerate} ids, and insures that they are all unique. Uses multiple
   * threads to generate the ids.
   *
   * @throws Exception
   */
  @Test
  public void testGenerateUniqueIdsMultipleThreads() throws Exception {
    long start = Calendar.getInstance().getTimeInMillis();
    // The threads used to generate ids
    Thread threads[] = new Thread[5];

    // HttpClient requires a ThreadSafeClientConnectionManager
    final ThreadSafeClientConnManager conman = new ThreadSafeClientConnManager();
    conman.setMaxTotal(50);
    conman.setDefaultMaxPerRoute(5);
    hc = new DefaultHttpClient(conman);

    assertEquals(
        "The number of threads ("
            + threads.length
            + ") must evenly divide into thenumber of ids to be "
            + "generated ("
            + countToGenerate
            + ")",
        0,
        countToGenerate % threads.length);
    final int generatePerThread = countToGenerate / threads.length;

    // Launch a thread, with each thread being responsible for generating a portion of the total ids
    for (int j = 0; j < threads.length; j++) {
      threads[j] =
          new Thread(
              new Runnable() {
                @Override
                public void run() {
                  for (int i = 0; i < generatePerThread; i++) {
                    double seed = Math.random();
                    log.trace("Seed is {}", seed);
                    Types t = selectType(seed, Types.values());
                    log.trace("Selected type {} with seed value {}", t, seed);

                    if (log.isDebugEnabled()) {
                      idTypeDistribution.putIfAbsent(t, new AtomicInteger(0));
                      idTypeDistribution.get(t).getAndAdd(1);
                    }

                    try {
                      generatedIds.add(reqFactory.createIdApiRequest(t).execute(hc));
                    } catch (IOException e) {
                      fail(e.getMessage());
                    }
                  }
                }
              },
              "ID Generation Thread " + j);
      threads[j].start();
    }

    // Wait for threads to stop
    for (int j = 0; j < threads.length; j++) {
      threads[j].join();
    }

    if (log.isDebugEnabled()) {
      StringBuilder sb = new StringBuilder("ID distribution:\n");
      int totalGenerated = 0;
      for (Types t : Types.values()) {
        final Integer typeTotal = idTypeDistribution.get(t).intValue();
        totalGenerated += typeTotal;
        sb.append("Type: ").append(t).append(" Count: ").append(typeTotal).append("\n");
      }
      sb.append("Total generated: ").append(totalGenerated).append("\n");
      sb.append("Unique generated: ").append(generatedIds.size()).append("\n");
      sb.append("Number of threads: ").append(threads.length).append("\n");
      sb.append("Execution time: ")
          .append(Calendar.getInstance().getTimeInMillis() - start)
          .append(" ms\n");
      log.debug(sb.toString());
    }

    // The number of generated IDs (stored in the Set) should equal 'countToGenerate'
    assertEquals(
        "Expected "
            + countToGenerate
            + " to be generated, but the Set contained "
            + generatedIds.size()
            + ".  Some ids may not have been unique.",
        countToGenerate,
        generatedIds.size());
  }