1 /* 2 * Copyright (C) 2019 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.invoker; 17 18 import com.android.tradefed.log.LogUtil.CLog; 19 import com.android.tradefed.util.FileUtil; 20 21 import com.google.common.collect.ImmutableMap; 22 23 import java.io.File; 24 import java.util.Map; 25 import java.util.concurrent.ConcurrentHashMap; 26 import java.util.concurrent.ConcurrentMap; 27 import java.util.concurrent.ConcurrentSkipListSet; 28 29 /** 30 * Files dependencies generated during the execution of a test or invocation that need to be carried 31 * for testing. This object is shared by all the invocation (tests, modules, etc.). 32 */ 33 public class ExecutionFiles { 34 35 /** Enumeration of known standard key for the map. */ 36 public static enum FilesKey { 37 ADB_BINARY, 38 // Describes the directory containing the build target tests artifacts. 39 TESTS_DIRECTORY, 40 // Sub-directory of TESTS_DIRECTORY that contains target artifacts 41 TARGET_TESTS_DIRECTORY, 42 // Sub-directory of TESTS_DIRECTORY that contains host-side artifacts 43 HOST_TESTS_DIRECTORY 44 } 45 46 private final ConcurrentMap<String, File> mFiles = new ConcurrentHashMap<>(); 47 private final ConcurrentSkipListSet<String> mShouldNotDelete = new ConcurrentSkipListSet<>(); 48 49 // Package private to avoid new instantiation. ExecutionFiles()50 ExecutionFiles() {} 51 52 /** 53 * Associates the specified value with the specified key in this map. 54 * 55 * @param key key with which the specified value is to be associated 56 * @param value value to be associated with the specified key 57 * @return the previous value associated with {@code key}, or {@code null} if there was no 58 * mapping for {@code key}. 59 * @see ConcurrentMap 60 */ put(String key, File value)61 public File put(String key, File value) { 62 return mFiles.put(key, value); 63 } 64 65 /** 66 * Variation of {@link #put(String, File)} with a known key. 67 * 68 * @param key key with which the specified value is to be associated 69 * @param value value to be associated with the specified key 70 * @return the previous value associated with {@code key}, or {@code null} if there was no 71 * mapping for {@code key}. 72 */ put(FilesKey key, File value)73 public File put(FilesKey key, File value) { 74 return mFiles.put(key.toString(), value); 75 } 76 77 /** 78 * Variation of {@link #put(FilesKey, File)} with option to prevent the file from being deleted 79 * at the end of the invocation. 80 * 81 * @param key key with which the specified value is to be associated 82 * @param value value to be associated with the specified key 83 * @param shouldNotDelete prevent the file from being deleted at the end of invocation. 84 * @return the previous value associated with {@code key}, or {@code null} if there was no 85 * mapping for {@code key}. 86 */ put(FilesKey key, File value, boolean shouldNotDelete)87 public File put(FilesKey key, File value, boolean shouldNotDelete) { 88 File f = mFiles.put(key.toString(), value); 89 if (shouldNotDelete) { 90 mShouldNotDelete.add(key.toString()); 91 } else { 92 mShouldNotDelete.remove(key.toString()); 93 } 94 if (f != null) { 95 CLog.w("Replaced key '%s' with value '%s' by '%s'", key, f, value); 96 } 97 return f; 98 } 99 100 /** 101 * If the specified key is not already associated with a value, associates it with the given 102 * value. 103 * 104 * @param key key with which the specified value is to be associated 105 * @param value value to be associated with the specified key 106 * @return the previous value associated with the specified key, or {@code null} if there was no 107 * mapping for the key. 108 */ putIfAbsent(String key, File value)109 public File putIfAbsent(String key, File value) { 110 return mFiles.putIfAbsent(key, value); 111 } 112 113 /** 114 * Copies all of the mappings from the specified map to this map. 115 * 116 * @param properties mappings to be stored in this map 117 * @return The final mapping 118 */ putAll(Map<String, File> properties)119 public ExecutionFiles putAll(Map<String, File> properties) { 120 mFiles.putAll(properties); 121 return this; 122 } 123 124 /** 125 * Copies all of the mappings from the specified map to this map. 126 * 127 * @param copyFrom original {@link ExecutionFiles} to copy from. 128 * @return The final mapping 129 */ putAll(ExecutionFiles copyFrom)130 public ExecutionFiles putAll(ExecutionFiles copyFrom) { 131 mFiles.putAll(copyFrom.getAll()); 132 mShouldNotDelete.addAll(copyFrom.mShouldNotDelete); 133 return this; 134 } 135 136 /** Returns whether or not the map of properties is empty. */ isEmpty()137 public boolean isEmpty() { 138 return mFiles.isEmpty(); 139 } 140 141 /** 142 * Returns the value to which the specified key is mapped, or {@code null} if this map contains 143 * no mapping for the key. 144 * 145 * @param key the key whose associated value is to be returned 146 * @return the value to which the specified key is mapped, or {@code null} if this map contains 147 * no mapping for the key 148 */ get(String key)149 public File get(String key) { 150 return mFiles.get(key); 151 } 152 153 /** 154 * Variation of {@link #get(String)} with a known key. 155 * 156 * @param key the key whose associated value is to be returned 157 * @return the value to which the specified key is mapped, or {@code null} if this map contains 158 * no mapping for the key 159 */ get(FilesKey key)160 public File get(FilesKey key) { 161 return mFiles.get(key.toString()); 162 } 163 164 /** 165 * Returns {@code true} if this map contains a mapping for the specified key. 166 * 167 * @param key key whose presence in this map is to be tested 168 * @return {@code true} if this map contains a mapping for the specified key 169 */ containsKey(String key)170 public boolean containsKey(String key) { 171 return mFiles.containsKey(key); 172 } 173 174 /** Returns all the properties in a copy of the map */ getAll()175 public ImmutableMap<String, File> getAll() { 176 return ImmutableMap.copyOf(mFiles); 177 } 178 179 /** 180 * Removes the mapping for a key from this map if it is present (optional operation). 181 * 182 * @param key key whose mapping is to be removed from the map 183 * @return the previous value associated with {@code key}, or {@code null} if there was no 184 * mapping for {@code key}. 185 */ remove(String key)186 public File remove(String key) { 187 return mFiles.remove(key); 188 } 189 190 /** Delete all the files that are tracked and not marked as 'should not delete'. */ clearFiles()191 public void clearFiles() { 192 for (String key : mFiles.keySet()) { 193 if (mShouldNotDelete.contains(key)) { 194 continue; 195 } 196 FileUtil.recursiveDelete(mFiles.get(key)); 197 mFiles.remove(key); 198 } 199 } 200 } 201