1 /* 2 * Copyright (C) 2017 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.tradefed.testtype.suite; 17 18 import com.android.tradefed.build.StubBuildProvider; 19 import com.android.tradefed.config.Configuration; 20 import com.android.tradefed.config.IConfiguration; 21 import com.android.tradefed.config.IDeviceConfiguration; 22 import com.android.tradefed.device.metric.FilePullerLogCollector; 23 import com.android.tradefed.device.metric.IMetricCollector; 24 import com.android.tradefed.result.TextResultReporter; 25 import com.android.tradefed.targetprep.ITargetPreparer; 26 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer; 27 import com.android.tradefed.testtype.suite.module.BaseModuleController; 28 import com.android.tradefed.util.FileUtil; 29 import com.android.tradefed.util.ModuleTestTypeUtil; 30 31 import java.io.File; 32 import java.io.IOException; 33 import java.util.ArrayList; 34 import java.util.List; 35 import java.util.regex.Matcher; 36 import java.util.regex.Pattern; 37 38 /** 39 * This class will help validating that the {@link IConfiguration} loaded for the suite are meeting 40 * the expected requirements: - No Build providers - No Result reporters 41 */ 42 public class ValidateSuiteConfigHelper { 43 44 /** 45 * Special exemption list for some collectors. They would be overly cumbersome to be defined at 46 * the suite level. 47 */ 48 private static final List<String> ALLOWED_COLLECTOR_IN_MODULE = new ArrayList<>(); 49 50 private static final String XML_COMMENT_REGEX = "<!--(\n|.)*?-->"; 51 // Matches both 'include' and 'template-include' tags. 52 private static final String INCLUDE_TAG_REGEX = "<\\s*(template-)?include\\s"; 53 private static Pattern mIncludeTagPattern = null; 54 55 static { 56 // This collector simply pull and log file from the device. it is useful at the module level 57 // so they can specify which 'key' is going to be watched to be pulled. FilePullerLogCollector.class.getCanonicalName()58 ALLOWED_COLLECTOR_IN_MODULE.add(FilePullerLogCollector.class.getCanonicalName()); 59 } 60 ValidateSuiteConfigHelper()61 private ValidateSuiteConfigHelper() {} 62 63 /** 64 * Check that a configuration is properly built to run in a suite. 65 * 66 * @param config a {@link IConfiguration} to be checked if valide for suite. 67 */ validateConfig(IConfiguration config)68 public static void validateConfig(IConfiguration config) { 69 if (config.getDeviceConfig().size() < 2) { 70 // Special handling for single device objects. 71 if (!config.getBuildProvider().getClass().isAssignableFrom(StubBuildProvider.class)) { 72 throwRuntime( 73 config, 74 String.format( 75 "%s objects are not allowed in module.", 76 Configuration.BUILD_PROVIDER_TYPE_NAME)); 77 } 78 checkTargetPrep(config, config.getTargetPreparers()); 79 } 80 // if a multi device config is presented, ensure none of the devices define a build_provider 81 for (IDeviceConfiguration deviceConfig : config.getDeviceConfig()) { 82 if (!deviceConfig 83 .getBuildProvider() 84 .getClass() 85 .isAssignableFrom(StubBuildProvider.class)) { 86 throwRuntime( 87 config, 88 String.format( 89 "%s objects are not allowed in module.", 90 Configuration.BUILD_PROVIDER_TYPE_NAME)); 91 } 92 checkTargetPrep(config, deviceConfig.getTargetPreparers()); 93 } 94 if (config.getTestInvocationListeners().size() != 1) { 95 throwRuntime( 96 config, 97 String.format( 98 "%s objects are not allowed in module.", 99 Configuration.RESULT_REPORTER_TYPE_NAME)); 100 } 101 if (!config.getTestInvocationListeners() 102 .get(0) 103 .getClass() 104 .isAssignableFrom(TextResultReporter.class)) { 105 throwRuntime( 106 config, 107 String.format( 108 "%s objects are not allowed in module.", 109 Configuration.RESULT_REPORTER_TYPE_NAME)); 110 } 111 // For now we do not allow pre-multi target preparers in modules. 112 if (!config.getMultiPreTargetPreparers().isEmpty()) { 113 throwRuntime( 114 config, 115 String.format( 116 "%s objects are not allowed in module.", 117 Configuration.MULTI_PRE_TARGET_PREPARER_TYPE_NAME)); 118 } 119 120 // Check multi target preparers 121 checkTargetPrep(config, config.getMultiTargetPreparers()); 122 123 // Check metric collectors if not a performance module 124 if (!ModuleTestTypeUtil.isPerformanceModule(config)) { 125 for (IMetricCollector collector : config.getMetricCollectors()) { 126 // Only some collectors are allowed in the module 127 if (!ALLOWED_COLLECTOR_IN_MODULE.contains( 128 collector.getClass().getCanonicalName())) { 129 throwRuntime( 130 config, 131 String.format( 132 "%s objects are not allowed in module. Except for: %s", 133 Configuration.DEVICE_METRICS_COLLECTOR_TYPE_NAME, 134 ALLOWED_COLLECTOR_IN_MODULE)); 135 } 136 } 137 138 if (!config.getPostProcessors().isEmpty()) { 139 throwRuntime( 140 config, 141 String.format( 142 "%s objects are not allowed in module.", 143 Configuration.METRIC_POST_PROCESSOR_TYPE_NAME)); 144 } 145 } 146 147 // Check that we validate the module_controller. 148 List<?> controllers = config.getConfigurationObjectList(ModuleDefinition.MODULE_CONTROLLER); 149 if (controllers != null) { 150 for (Object controller : controllers) { 151 if (!(controller instanceof BaseModuleController)) { 152 throwRuntime( 153 config, 154 String.format( 155 "%s object should be of type %s", 156 ModuleDefinition.MODULE_CONTROLLER, 157 BaseModuleController.class)); 158 } 159 } 160 } 161 } 162 163 /** 164 * Check that a module config file is valid - no templates or includes 165 * 166 * @param configFile a config {@link File} to be validated for a suite. 167 */ validateConfigFile(File configFile)168 public static void validateConfigFile(File configFile) { 169 try { 170 if (mIncludeTagPattern == null) { 171 mIncludeTagPattern = Pattern.compile(INCLUDE_TAG_REGEX); 172 } 173 174 // Read config and remove all xml comments 175 String source = FileUtil.readStringFromFile(configFile); 176 source = source.replaceAll(XML_COMMENT_REGEX, ""); 177 178 // Find matches for 'template-include' or 'include' tags. 179 Matcher matcher = mIncludeTagPattern.matcher(source); 180 if (matcher.find()) { 181 throw new RuntimeException( 182 String.format( 183 "Found template-include or include tag in config file: %s", 184 configFile.getAbsolutePath())); 185 } 186 } catch (IOException e) { 187 throw new RuntimeException( 188 String.format( 189 "Failed to read file %s with exception %s", 190 configFile.getAbsolutePath(), e)); 191 } 192 } 193 194 /** 195 * Check target_preparer and multi_target_preparer to ensure they do not extends each other as 196 * it could lead to some issues. 197 */ checkTargetPrep(IConfiguration config, List<?> targetPrepList)198 private static boolean checkTargetPrep(IConfiguration config, List<?> targetPrepList) { 199 for (Object o : targetPrepList) { 200 if (o instanceof ITargetPreparer && o instanceof IMultiTargetPreparer) { 201 throwRuntime( 202 config, 203 String.format( 204 "%s is extending both %s and " + "%s", 205 o.getClass().getCanonicalName(), 206 Configuration.TARGET_PREPARER_TYPE_NAME, 207 Configuration.MULTI_PREPARER_TYPE_NAME)); 208 return false; 209 } 210 } 211 return true; 212 } 213 throwRuntime(IConfiguration config, String msg)214 private static void throwRuntime(IConfiguration config, String msg) { 215 throw new RuntimeException( 216 String.format( 217 "Configuration %s cannot be run in a suite: %s", config.getName(), msg)); 218 } 219 } 220