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