1 /* 2 * Copyright (C) 2012 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.caliper.runner; 18 19 import static java.util.concurrent.TimeUnit.MINUTES; 20 import static org.junit.Assert.assertEquals; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 24 import com.google.caliper.Benchmark; 25 import com.google.caliper.config.VmConfig; 26 import com.google.caliper.model.BenchmarkSpec; 27 import com.google.caliper.platform.jvm.JvmPlatform; 28 import com.google.caliper.worker.WorkerMain; 29 import com.google.common.collect.ImmutableMap; 30 import com.google.common.collect.ImmutableSet; 31 import com.google.common.collect.Iterables; 32 import com.google.common.collect.Sets; 33 34 import org.junit.Before; 35 import org.junit.Test; 36 import org.junit.runner.RunWith; 37 import org.junit.runners.JUnit4; 38 39 import java.io.File; 40 import java.lang.reflect.Method; 41 import java.util.Arrays; 42 import java.util.List; 43 import java.util.Set; 44 import java.util.UUID; 45 46 /** 47 * Tests {@link WorkerProcess}. 48 * 49 * <p>TODO(lukes,gak): write more tests for how our specs get turned into commandlines 50 */ 51 52 @RunWith(JUnit4.class) 53 public class WorkerProcessTest { 54 private static final int PORT_NUMBER = 4004; 55 private static final UUID TRIAL_ID = UUID.randomUUID(); 56 57 private static class MockRegistrar implements ShutdownHookRegistrar { 58 Set<Thread> hooks = Sets.newHashSet(); addShutdownHook(Thread hook)59 @Override public void addShutdownHook(Thread hook) { 60 hooks.add(hook); 61 } removeShutdownHook(Thread hook)62 @Override public boolean removeShutdownHook(Thread hook) { 63 return hooks.remove(hook); 64 } 65 } 66 67 private final MockRegistrar registrar = new MockRegistrar(); 68 private BenchmarkClass benchmarkClass; 69 setUp()70 @Before public void setUp() throws InvalidBenchmarkException { 71 benchmarkClass = BenchmarkClass.forClass(TestBenchmark.class); 72 } 73 simpleArgsTest()74 @Test public void simpleArgsTest() throws Exception { 75 Method method = TestBenchmark.class.getDeclaredMethods()[0]; 76 AllocationInstrument allocationInstrument = new AllocationInstrument(); 77 allocationInstrument.setOptions(ImmutableMap.of("trackAllocations", "true")); 78 VmConfig vmConfig = new VmConfig( 79 new File("foo"), 80 Arrays.asList("--doTheHustle"), 81 new File("java"), 82 new JvmPlatform()); 83 Experiment experiment = new Experiment( 84 allocationInstrument.createInstrumentation(method), 85 ImmutableMap.<String, String>of(), 86 new VirtualMachine("foo-jvm", vmConfig)); 87 BenchmarkSpec spec = new BenchmarkSpec.Builder() 88 .className(TestBenchmark.class.getName()) 89 .methodName(method.getName()) 90 .build(); 91 ProcessBuilder builder = createProcess(experiment, spec); 92 List<String> commandLine = builder.command(); 93 assertEquals(new File("java").getAbsolutePath(), commandLine.get(0)); 94 assertEquals("--doTheHustle", commandLine.get(1)); // vm specific flags come next 95 assertEquals("-cp", commandLine.get(2)); // then the classpath 96 // should we assert on classpath contents? 97 ImmutableSet<String> extraCommandLineArgs = 98 allocationInstrument.getExtraCommandLineArgs(vmConfig); 99 assertEquals(extraCommandLineArgs.asList(), 100 commandLine.subList(4, 4 + extraCommandLineArgs.size())); 101 int index = 4 + extraCommandLineArgs.size(); 102 assertEquals("-XX:+PrintFlagsFinal", commandLine.get(index)); 103 assertEquals("-XX:+PrintCompilation", commandLine.get(++index)); 104 assertEquals("-XX:+PrintGC", commandLine.get(++index)); 105 assertEquals(WorkerMain.class.getName(), commandLine.get(++index)); 106 // followed by worker args... 107 } 108 shutdownHook_waitFor()109 @Test public void shutdownHook_waitFor() throws Exception { 110 Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker(); 111 assertEquals("worker-shutdown-hook-" + TRIAL_ID, 112 Iterables.getOnlyElement(registrar.hooks).getName()); 113 worker.waitFor(); 114 assertTrue(registrar.hooks.isEmpty()); 115 } 116 shutdownHook_exitValueThrows()117 @Test public void shutdownHook_exitValueThrows() throws Exception { 118 Process worker = createWorkerProcess( 119 FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker(); 120 try { 121 Thread hook = Iterables.getOnlyElement(registrar.hooks); 122 assertEquals("worker-shutdown-hook-" + TRIAL_ID, hook.getName()); 123 try { 124 worker.exitValue(); 125 fail(); 126 } catch (IllegalThreadStateException expected) {} 127 assertTrue(registrar.hooks.contains(hook)); 128 } finally { 129 worker.destroy(); // clean up 130 } 131 } 132 shutdownHook_exitValue()133 @Test public void shutdownHook_exitValue() throws Exception { 134 Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker(); 135 while (true) { 136 try { 137 worker.exitValue(); 138 assertTrue(registrar.hooks.isEmpty()); 139 break; 140 } catch (IllegalThreadStateException e) { 141 Thread.sleep(10); // keep polling 142 } 143 } 144 } 145 shutdownHook_destroy()146 @Test public void shutdownHook_destroy() throws Exception { 147 Process worker = createWorkerProcess( 148 FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker(); 149 worker.destroy(); 150 assertTrue(registrar.hooks.isEmpty()); 151 } 152 153 static final class TestBenchmark { thing(long reps)154 @Benchmark long thing(long reps) { 155 long dummy = 0; 156 for (long i = 0; i < reps; i++) { 157 dummy += new Long(dummy).hashCode(); 158 } 159 return dummy; 160 } 161 } 162 createProcess(Experiment experiment, BenchmarkSpec benchmarkSpec)163 private ProcessBuilder createProcess(Experiment experiment, BenchmarkSpec benchmarkSpec) { 164 return WorkerProcess.buildProcess(TRIAL_ID, experiment, benchmarkSpec, PORT_NUMBER, 165 benchmarkClass); 166 } 167 createWorkerProcess(Class<?> main, String ...args)168 private WorkerProcess createWorkerProcess(Class<?> main, String ...args) { 169 return new WorkerProcess(FakeWorkers.createProcessBuilder(main, args), 170 TRIAL_ID, 171 null, 172 registrar); 173 } 174 } 175