blob: 397f7d8cb8ed9f066a6f68e1adc4ca3ceffe976c [file] [log] [blame]
/*
* Copyright (C) 2012 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;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.caliper.Benchmark;
import com.google.caliper.config.VmConfig;
import com.google.caliper.model.BenchmarkSpec;
import com.google.caliper.platform.jvm.JvmPlatform;
import com.google.caliper.worker.WorkerMain;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.UUID;
/**
* Tests {@link WorkerProcess}.
*
* <p>TODO(lukes,gak): write more tests for how our specs get turned into commandlines
*/
@RunWith(JUnit4.class)
public class WorkerProcessTest {
private static final int PORT_NUMBER = 4004;
private static final UUID TRIAL_ID = UUID.randomUUID();
private static class MockRegistrar implements ShutdownHookRegistrar {
Set<Thread> hooks = Sets.newHashSet();
@Override public void addShutdownHook(Thread hook) {
hooks.add(hook);
}
@Override public boolean removeShutdownHook(Thread hook) {
return hooks.remove(hook);
}
}
private final MockRegistrar registrar = new MockRegistrar();
private BenchmarkClass benchmarkClass;
@Before public void setUp() throws InvalidBenchmarkException {
benchmarkClass = BenchmarkClass.forClass(TestBenchmark.class);
}
@Test public void simpleArgsTest() throws Exception {
Method method = TestBenchmark.class.getDeclaredMethods()[0];
AllocationInstrument allocationInstrument = new AllocationInstrument();
allocationInstrument.setOptions(ImmutableMap.of("trackAllocations", "true"));
VmConfig vmConfig = new VmConfig(
new File("foo"),
Arrays.asList("--doTheHustle"),
new File("java"),
new JvmPlatform());
Experiment experiment = new Experiment(
allocationInstrument.createInstrumentation(method),
ImmutableMap.<String, String>of(),
new VirtualMachine("foo-jvm", vmConfig));
BenchmarkSpec spec = new BenchmarkSpec.Builder()
.className(TestBenchmark.class.getName())
.methodName(method.getName())
.build();
ProcessBuilder builder = createProcess(experiment, spec);
List<String> commandLine = builder.command();
assertEquals(new File("java").getAbsolutePath(), commandLine.get(0));
assertEquals("--doTheHustle", commandLine.get(1)); // vm specific flags come next
assertEquals("-cp", commandLine.get(2)); // then the classpath
// should we assert on classpath contents?
ImmutableSet<String> extraCommandLineArgs =
allocationInstrument.getExtraCommandLineArgs(vmConfig);
assertEquals(extraCommandLineArgs.asList(),
commandLine.subList(4, 4 + extraCommandLineArgs.size()));
int index = 4 + extraCommandLineArgs.size();
assertEquals("-XX:+PrintFlagsFinal", commandLine.get(index));
assertEquals("-XX:+PrintCompilation", commandLine.get(++index));
assertEquals("-XX:+PrintGC", commandLine.get(++index));
assertEquals(WorkerMain.class.getName(), commandLine.get(++index));
// followed by worker args...
}
@Test public void shutdownHook_waitFor() throws Exception {
Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker();
assertEquals("worker-shutdown-hook-" + TRIAL_ID,
Iterables.getOnlyElement(registrar.hooks).getName());
worker.waitFor();
assertTrue(registrar.hooks.isEmpty());
}
@Test public void shutdownHook_exitValueThrows() throws Exception {
Process worker = createWorkerProcess(
FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker();
try {
Thread hook = Iterables.getOnlyElement(registrar.hooks);
assertEquals("worker-shutdown-hook-" + TRIAL_ID, hook.getName());
try {
worker.exitValue();
fail();
} catch (IllegalThreadStateException expected) {}
assertTrue(registrar.hooks.contains(hook));
} finally {
worker.destroy(); // clean up
}
}
@Test public void shutdownHook_exitValue() throws Exception {
Process worker = createWorkerProcess(FakeWorkers.Exit.class, "0").startWorker();
while (true) {
try {
worker.exitValue();
assertTrue(registrar.hooks.isEmpty());
break;
} catch (IllegalThreadStateException e) {
Thread.sleep(10); // keep polling
}
}
}
@Test public void shutdownHook_destroy() throws Exception {
Process worker = createWorkerProcess(
FakeWorkers.Sleeper.class, Long.toString(MINUTES.toMillis(1))).startWorker();
worker.destroy();
assertTrue(registrar.hooks.isEmpty());
}
static final class TestBenchmark {
@Benchmark long thing(long reps) {
long dummy = 0;
for (long i = 0; i < reps; i++) {
dummy += new Long(dummy).hashCode();
}
return dummy;
}
}
private ProcessBuilder createProcess(Experiment experiment, BenchmarkSpec benchmarkSpec) {
return WorkerProcess.buildProcess(TRIAL_ID, experiment, benchmarkSpec, PORT_NUMBER,
benchmarkClass);
}
private WorkerProcess createWorkerProcess(Class<?> main, String ...args) {
return new WorkerProcess(FakeWorkers.createProcessBuilder(main, args),
TRIAL_ID,
null,
registrar);
}
}