/**
   * Time a multi-threaded access to a cache.
   *
   * @return the timing stopwatch
   */
  private <V> StopWatch timeMultiThreaded(
      String id, final Map<Integer, V> map, ValueFactory<V> factory) throws InterruptedException {

    StopWatch stopWatch = new StopWatch(id);
    for (int i = 0; i < 500; i++) {
      map.put(i, factory.newValue(i));
    }
    Thread[] threads = new Thread[30];
    stopWatch.start("Running threads");
    for (int threadIndex = 0; threadIndex < threads.length; threadIndex++) {
      threads[threadIndex] =
          new Thread("Cache access thread " + threadIndex) {
            @Override
            public void run() {
              for (int j = 0; j < 1000; j++) {
                for (int i = 0; i < 1000; i++) {
                  map.get(i);
                }
              }
            }
          };
    }
    for (Thread thread : threads) {
      thread.start();
    }

    for (Thread thread : threads) {
      if (thread.isAlive()) {
        thread.join(2000);
      }
    }
    stopWatch.stop();
    return stopWatch;
  }
  @Test
  public void testConstructorString() {
    StopWatch s = new StopWatch("0:0:0");
    assertEquals(s.toString(), "0:00:000");

    s = new StopWatch("1:1:1");
    assertEquals(s.toString(), "1:01:001");

    s = new StopWatch("9999:59:999");
    assertEquals(s.toString(), "9999:59:999");
  }
  @Test
  public void testConstructorMilli() {
    StopWatch s = new StopWatch(0);
    assertEquals(s.toString(), "0:00:000");

    s = new StopWatch(1);
    assertEquals(s.toString(), "0:00:001");

    s = new StopWatch(999);
    assertEquals(s.toString(), "0:00:999");
  }
  @Test
  public void testConstructor() {
    StopWatch s = new StopWatch(5, 10, 300);
    assertEquals(s.toString(), "5:10:300");

    s = new StopWatch("20:10:8");
    assertEquals(s.toString(), "20:10:008");

    s = new StopWatch("20:8");
    assertEquals(s.toString(), "0:20:008");

    s = new StopWatch("8");
    assertEquals(s.toString(), "0:00:008");
  }
  @Test
  public void testConstructorSec() {
    StopWatch s = new StopWatch(0, 0);
    assertEquals(s.toString(), "0:00:000");

    s = new StopWatch(1, 0);
    assertEquals(s.toString(), "0:01:000");

    s = new StopWatch(1, 1);
    assertEquals(s.toString(), "0:01:001");

    s = new StopWatch(59, 999);
    assertEquals(s.toString(), "0:59:999");
  }
  @Test
  public void testToString() {
    StopWatch s1 = new StopWatch();
    assertEquals(s1.toString(), "0:00:000");

    StopWatch s2 = new StopWatch(1234, 53, 867);
    assertEquals(s2.toString(), "1234:53:867");

    StopWatch s3 = new StopWatch(0, 0, 0);
    assertEquals(s3.toString(), "0:00:000");

    StopWatch s4 = new StopWatch(1, 1, 1);
    assertEquals(s4.toString(), "1:01:001");

    StopWatch s5 = new StopWatch(22, 30, 900);
    assertEquals(s5.toString(), "22:30:900");
  }
  @Test
  @Ignore("Intended for use during development only")
  public void shouldBeFasterThanSynchronizedMap() throws Exception {
    Map<Integer, WeakReference<String>> synchronizedMap =
        Collections.synchronizedMap(new WeakHashMap<Integer, WeakReference<String>>());
    StopWatch mapTime =
        timeMultiThreaded(
            "SynchronizedMap",
            synchronizedMap,
            new ValueFactory<WeakReference<String>>() {

              @Override
              public WeakReference<String> newValue(int v) {
                return new WeakReference<String>(String.valueOf(v));
              }
            });
    System.out.println(mapTime.prettyPrint());

    this.map.setDisableTestHooks(true);
    StopWatch cacheTime =
        timeMultiThreaded(
            "WeakConcurrentCache",
            this.map,
            new ValueFactory<String>() {

              @Override
              public String newValue(int v) {
                return String.valueOf(v);
              }
            });
    System.out.println(cacheTime.prettyPrint());

    // We should be at least 4 time faster
    assertThat(cacheTime.getTotalTimeSeconds(), is(lessThan(mapTime.getTotalTimeSeconds() / 4.0)));
  }
  @Test
  public void testCompareTo() {
    StopWatch s1 = new StopWatch(5, 59, 300);
    StopWatch s2 = new StopWatch(6, 01, 200);
    StopWatch s3 = new StopWatch(5, 50, 200);
    StopWatch s4 = new StopWatch(5, 59, 300);

    assertFalse(s1.equals(s2));
    assertTrue(s1.equals(s4));

    assertTrue(s2.compareTo(s1) > 0);
    assertTrue(s3.compareTo(s1) < 0);
    assertTrue(s1.compareTo(s4) == 0);
  }
  @Test
  public void testMutate() {
    StopWatch s1 = new StopWatch(5, 59, 300);
    StopWatch s2 = new StopWatch(5, 59, 300);

    StopWatch.setMutate(false);

    s1.add(1000);
    s1.setMilliseconds(100);
    s1.setSeconds(50);
    s1.setMinutes(2);
    s1.add(s2);

    assertTrue(s1.equals(s2));
  }
  @Test
  public void testLoadSave() {
    StopWatch s1 = new StopWatch(5, 59, 300);
    assertEquals(s1.toString(), "5:59:300");

    s1.save("file1");
    s1 = new StopWatch();
    assertEquals(s1.toString(), "0:00:000");

    s1.load("file1");
    assertEquals(s1.toString(), "5:59:300");
  }
  @Test
  public void testAddMethod() {
    StopWatch s1 = new StopWatch(5, 59, 300);
    s1.add(2000);
    assertEquals(s1.toString(), "6:01:300");

    s1 = new StopWatch(5, 59, 300);
    StopWatch s2 = new StopWatch(2, 2, 300);
    s1.add(s2);
    System.out.println(s1);
    assertEquals(s1.toString(), "8:01:600");

    for (int i = 0; i < 15000; i++) s1.inc();
    System.out.println(s1);
    assertEquals(s1.toString(), "8:16:600");
  }
  @Test
  public void testEqual2() {
    StopWatch s1 = new StopWatch(5, 59, 300);
    StopWatch s2 = new StopWatch(6, 01, 200);
    StopWatch s3 = new StopWatch(5, 50, 200);
    StopWatch s4 = new StopWatch(5, 59, 300);

    assertFalse(StopWatch.equals(s1, s2));
    assertFalse(StopWatch.equals(s1, s3));
    assertTrue(StopWatch.equals(s1, s4));
    assertFalse(StopWatch.equals(s2, s3));
    assertFalse(StopWatch.equals(s2, s4));
    assertFalse(StopWatch.equals(s3, s4));
  }
 @Test
 public void testDefaultConstructor() {
   StopWatch s = new StopWatch();
   assertEquals(s.toString(), "0:00:000");
 }
  @Test
  public void getUniqueDeclaredMethods_isFastEnough() {
    Assume.group(TestGroup.PERFORMANCE);

    @SuppressWarnings("unused")
    class C {
      void m00() {}

      void m01() {}

      void m02() {}

      void m03() {}

      void m04() {}

      void m05() {}

      void m06() {}

      void m07() {}

      void m08() {}

      void m09() {}

      void m10() {}

      void m11() {}

      void m12() {}

      void m13() {}

      void m14() {}

      void m15() {}

      void m16() {}

      void m17() {}

      void m18() {}

      void m19() {}

      void m20() {}

      void m21() {}

      void m22() {}

      void m23() {}

      void m24() {}

      void m25() {}

      void m26() {}

      void m27() {}

      void m28() {}

      void m29() {}

      void m30() {}

      void m31() {}

      void m32() {}

      void m33() {}

      void m34() {}

      void m35() {}

      void m36() {}

      void m37() {}

      void m38() {}

      void m39() {}

      void m40() {}

      void m41() {}

      void m42() {}

      void m43() {}

      void m44() {}

      void m45() {}

      void m46() {}

      void m47() {}

      void m48() {}

      void m49() {}

      void m50() {}

      void m51() {}

      void m52() {}

      void m53() {}

      void m54() {}

      void m55() {}

      void m56() {}

      void m57() {}

      void m58() {}

      void m59() {}

      void m60() {}

      void m61() {}

      void m62() {}

      void m63() {}

      void m64() {}

      void m65() {}

      void m66() {}

      void m67() {}

      void m68() {}

      void m69() {}

      void m70() {}

      void m71() {}

      void m72() {}

      void m73() {}

      void m74() {}

      void m75() {}

      void m76() {}

      void m77() {}

      void m78() {}

      void m79() {}

      void m80() {}

      void m81() {}

      void m82() {}

      void m83() {}

      void m84() {}

      void m85() {}

      void m86() {}

      void m87() {}

      void m88() {}

      void m89() {}

      void m90() {}

      void m91() {}

      void m92() {}

      void m93() {}

      void m94() {}

      void m95() {}

      void m96() {}

      void m97() {}

      void m98() {}

      void m99() {}
    }

    StopWatch sw = new StopWatch();
    sw.start();
    Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(C.class);
    sw.stop();
    long totalMs = sw.getTotalTimeMillis();
    assertThat(methods.length, Matchers.greaterThan(100));
    assertThat(totalMs, Matchers.lessThan(10L));
  }