1 /* 2 * Copyright (C) 2022 The Android Open Source Project 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 package com.android.cuttlefish.test; 17 18 import static com.google.common.base.Preconditions.checkNotNull; 19 import static com.google.common.base.Preconditions.checkState; 20 21 import com.android.tradefed.build.IBuildInfo; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.invoker.TestInformation; 24 import com.android.tradefed.testtype.HostTest; 25 import com.android.tradefed.testtype.IBuildReceiver; 26 import com.android.tradefed.testtype.ISetOptionReceiver; 27 import com.android.tradefed.testtype.ITestInformationReceiver; 28 import com.google.auto.value.AutoAnnotation; 29 import com.google.common.collect.ImmutableMap; 30 import com.google.inject.AbstractModule; 31 import com.google.inject.Binder; 32 import com.google.inject.Guice; 33 import com.google.inject.Injector; 34 import java.io.File; 35 import java.io.FileNotFoundException; 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.regex.Pattern; 42 import org.junit.runners.BlockJUnit4ClassRunner; 43 import org.junit.runners.model.InitializationError; 44 import org.junit.runners.model.TestClass; 45 46 public final class CuttlefishIntegrationTestRunner extends BlockJUnit4ClassRunner 47 implements ITestInformationReceiver, ISetOptionReceiver, IBuildReceiver { 48 @Option(name = HostTest.SET_OPTION_NAME, description = HostTest.SET_OPTION_DESC) 49 private HashSet<String> keyValueOptions = new HashSet<>(); 50 51 private IBuildInfo buildInfo; 52 private TestInformation testInfo; 53 private final TestClass testClass; 54 55 // Required by JUnit CuttlefishIntegrationTestRunner(Class<?> testClass)56 public CuttlefishIntegrationTestRunner(Class<?> testClass) throws InitializationError { 57 this(new TestClass(testClass)); 58 } 59 CuttlefishIntegrationTestRunner(TestClass testClass)60 private CuttlefishIntegrationTestRunner(TestClass testClass) throws InitializationError { 61 super(testClass); 62 this.testClass = testClass; 63 } 64 65 @Override setBuild(IBuildInfo buildInfo)66 public void setBuild(IBuildInfo buildInfo) { 67 this.buildInfo = checkNotNull(buildInfo); 68 } 69 70 @Override setTestInformation(TestInformation testInfo)71 public void setTestInformation(TestInformation testInfo) { 72 this.testInfo = checkNotNull(testInfo); 73 } 74 75 @Override getTestInformation()76 public TestInformation getTestInformation() { 77 return checkNotNull(testInfo); 78 } 79 processOptions()80 private ImmutableMap<String, String> processOptions() { 81 // Regex from HostTest.setOptionToLoadedObject 82 String delim = ":"; 83 String esc = "\\"; 84 String regex = "(?<!" + Pattern.quote(esc) + ")" + Pattern.quote(delim); 85 ImmutableMap.Builder<String, String> optMap = ImmutableMap.builder(); 86 for (String item : keyValueOptions) { 87 String[] fields = item.split(regex); 88 checkState(fields.length == 2, "Could not parse \"%s\"", item); 89 String value = fields[1].replaceAll(Pattern.quote(esc) + Pattern.quote(delim), delim); 90 optMap.put(fields[0], value); 91 } 92 return optMap.build(); 93 } 94 95 @AutoAnnotation setOptionAnnotation(String value)96 private static SetOption setOptionAnnotation(String value) { 97 return new AutoAnnotation_CuttlefishIntegrationTestRunner_setOptionAnnotation(value); 98 } 99 bindOptions(Binder binder)100 private void bindOptions(Binder binder) { 101 // TODO(schuffelen): Handle collections and maps 102 for (Map.Entry<String, String> option : processOptions().entrySet()) { 103 SetOption annotation = setOptionAnnotation(option.getKey()); 104 binder.bind(String.class).annotatedWith(annotation).toInstance(option.getValue()); 105 } 106 } 107 108 private final class TradefedClassesModule extends AbstractModule { 109 @Override configure()110 protected void configure() { 111 bind(TestInformation.class).toInstance(testInfo); 112 bind(IBuildInfo.class).toInstance(buildInfo); 113 bindOptions(binder()); 114 } 115 } 116 117 @Override createTest()118 protected Object createTest() { 119 Injector injector = Guice.createInjector(new TradefedClassesModule()); 120 return injector.getInstance(testClass.getJavaClass()); 121 } 122 } 123