1/* 2 * Copyright (C) 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15package com.google.caliper.runner; 16 17import static com.google.common.base.Preconditions.checkState; 18 19import com.google.caliper.config.InvalidConfigurationException; 20import com.google.caliper.model.Trial; 21import com.google.caliper.util.InvalidCommandException; 22import com.google.common.collect.ImmutableList; 23import com.google.common.collect.Lists; 24import com.google.common.io.Files; 25 26import org.junit.rules.TestWatcher; 27import org.junit.runner.Description; 28 29import java.io.File; 30import java.io.PrintWriter; 31import java.io.StringWriter; 32import java.util.Arrays; 33import java.util.List; 34 35/** 36 * A {@link TestWatcher} that can be used to configure a run of caliper. 37 * 38 * <p>Provides common test configuration utilities and redirects output to a buffer and only dumps 39 * it during a failure. 40 * 41 * <p>TODO(lukes,gak): This is a bad name since it isn't just watching the tests, it is helping you 42 * run the tests. 43 */ 44public final class CaliperTestWatcher extends TestWatcher { 45 // N.B. StringWriter is internally synchronized and is safe to write to from multiple threads. 46 private StringWriter stdout; 47 private final StringWriter stderr = new StringWriter(); 48 private File workerOutput; 49 50 private String instrument; 51 private Class<?> benchmarkClass; 52 private List<String> extraOptions = Lists.newArrayList(); 53 54 CaliperTestWatcher forBenchmark(Class<?> benchmarkClass) { 55 this.benchmarkClass = benchmarkClass; 56 return this; 57 } 58 59 CaliperTestWatcher instrument(String instrument) { 60 this.instrument = instrument; 61 return this; 62 } 63 64 CaliperTestWatcher options(String... extraOptions) { 65 this.extraOptions = Arrays.asList(extraOptions); 66 return this; 67 } 68 69 void run() throws InvalidCommandException, InvalidBenchmarkException, 70 InvalidConfigurationException { 71 checkState(benchmarkClass != null, "You must configure a benchmark!"); 72 workerOutput = Files.createTempDir(); 73 // configure a custom dir so the files aren't deleted when CaliperMain returns 74 List<String> options = Lists.newArrayList( 75 "-Cworker.output=" + workerOutput.getPath(), 76 "-Cresults.file.class=", 77 "-Cresults.upload.class=" + InMemoryResultsUploader.class.getName()); 78 if (instrument != null) { 79 options.add("-i"); 80 options.add(instrument); 81 } 82 options.addAll(extraOptions); 83 options.add(benchmarkClass.getName()); 84 this.stdout = new StringWriter(); 85 CaliperMain.exitlessMain( 86 options.toArray(new String[0]), 87 new PrintWriter(stdout, true), 88 new PrintWriter(stderr, true)); 89 } 90 91 @Override protected void finished(Description description) { 92 if (workerOutput != null) { 93 for (File f : workerOutput.listFiles()) { 94 f.delete(); 95 } 96 workerOutput.delete(); 97 } 98 } 99 100 @Override protected void failed(Throwable e, Description description) { 101 // don't log if run was never called. 102 if (stdout != null) { 103 System.err.println("Caliper failed with the following output (stdout):\n" 104 + stdout.toString() + "stderr:\n" + stderr.toString()); 105 } 106 } 107 108 ImmutableList<Trial> trials() { 109 return InMemoryResultsUploader.trials(); 110 } 111 112 public StringWriter getStderr() { 113 return stderr; 114 } 115 116 public StringWriter getStdout() { 117 return stdout; 118 } 119 120 File workerOutputDirectory() { 121 return workerOutput; 122 } 123} 124