/*
 * Copyright (C) 2013 Google Inc.
 *
 * 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.google.caliper.runner.instrument;

import static org.junit.Assert.assertEquals;

import com.google.caliper.AfterExperiment;
import com.google.caliper.BeforeExperiment;
import com.google.caliper.Benchmark;
import com.google.caliper.api.AfterRep;
import com.google.caliper.api.BeforeRep;
import com.google.caliper.model.Measurement;
import com.google.caliper.model.Trial;
import com.google.caliper.runner.config.VmConfig;
import com.google.caliper.runner.config.VmType;
import com.google.caliper.runner.testing.CaliperTestWatcher;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import java.io.File;
import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Tests {@link AllocationInstrument}. */

@RunWith(JUnit4.class)
public class AllocationInstrumentTest {

  @Rule public final CaliperTestWatcher runner = new CaliperTestWatcher();

  @Test
  public void getExtraCommandLineArgs() throws Exception {
    AllocationInstrument instrument = new AllocationInstrument();
    File fakeJar = File.createTempFile("fake", "jar");
    fakeJar.deleteOnExit();
    instrument.setOptions(ImmutableMap.of("allocationAgentJar", fakeJar.getAbsolutePath()));
    ImmutableSet<String> expected =
        ImmutableSet.of(
            "-Xint",
            "-javaagent:" + fakeJar.getAbsolutePath(),
            "-Xbootclasspath/a:" + fakeJar.getAbsolutePath());
    VmConfig vmConfig =
        VmConfig.builder()
            .name("foo")
            .type(VmType.JVM)
            .home(System.getProperty("java.home"))
            .build();
    assertEquals(expected, instrument.getExtraCommandLineArgs(vmConfig));
    fakeJar.delete();
  }

  @Test
  public void intrinsics() throws Exception {
    runner.forBenchmark(ArrayListGrowthBenchmark.class).instrument("allocation").run();
    Trial trial = Iterables.getOnlyElement(runner.trials());
    ImmutableListMultimap<String, Measurement> measurementsByDescription =
        Measurement.indexByDescription(trial.measurements());
    // 14 objects and 1960 bytes are the known values for growing an ArrayList from 1 element to 100
    // elements
    for (Measurement objectMeasurement : measurementsByDescription.get("objects")) {
      assertEquals(14.0, objectMeasurement.value().magnitude() / objectMeasurement.weight(), 0.001);
    }
    for (Measurement byteMeasurement : measurementsByDescription.get("bytes")) {
      assertEquals(1960.0, byteMeasurement.value().magnitude() / byteMeasurement.weight(), 0.001);
    }
  }

  public static class TestBenchmark {
    List<Object> list = Lists.newLinkedList();

    @Benchmark
    public int compressionSize(int reps) {
      for (int i = 0; i < reps; i++) {
        list.add(new Object());
      }
      int hashCode = list.hashCode();
      list.clear();
      return hashCode;
    }
  }

  public static class TestMacroBenchmark {
    int beforeRepCalls = 0;
    int afterRepCalls = 0;
    int repCalls = 0;
    int hashCode = 0;

    @BeforeRep
    public void beforeRep() {
      beforeRepCalls++;
    }

    @Benchmark
    public void benchmark() {
      repCalls++;
      assertEquals(beforeRepCalls, afterRepCalls + 1);
      assertEquals(beforeRepCalls, repCalls);
      Object o = new Object();
      hashCode = o.hashCode();
    }

    @AfterRep
    public void afterRep() {
      afterRepCalls++;
    }

    @AfterExperiment
    public void afterExperiment() {
      assertEquals(repCalls, afterRepCalls);
      assertEquals(afterRepCalls, beforeRepCalls);
    }
  }

  public static class ArrayListGrowthBenchmark {
    @BeforeExperiment
    void warmUp() {
      // ensure that hotspot has compiled this code
      benchmarkGrowth(100000);
    }

    @Benchmark
    void benchmarkGrowth(int reps) {
      for (int i = 0; i < reps; i++) {
        List<String> list = Lists.newArrayListWithCapacity(1);
        for (int j = 0; j < 100; j++) {
          list.add("");
        }
      }
    }
  }
}

@@ -3370,56 +3370,8 @@
 k %7B%0A
-    List%3CObject%3E list = Lists.newLinkedList();%0A%0A
     
@@ -3416,32 +3416,54 @@
 ize(int reps) %7B%0A
+      int result = 0;%0A
       for (int i
@@ -3493,25 +3493,26 @@
         
-list.add(
+result %5E= 
 new Obje
@@ -3519,44 +3519,8 @@
 ct()
-);%0A      %7D%0A      int hashCode = list
 .has
@@ -3538,21 +3538,9 @@
     
-list.clear();
+%7D
 %0A   
@@ -3549,24 +3549,22 @@
  return 
-hashCode
+result
 ;%0A    %7D%0A

Fix Caliper tests.