• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.build.BuildInfo;
19 import com.android.tradefed.build.BuildSerializedVersion;
20 import com.android.tradefed.build.IBuildInfo;
21 import com.android.tradefed.build.proto.BuildInformation;
22 import com.android.tradefed.config.ConfigurationDescriptor;
23 import com.android.tradefed.config.proto.ConfigurationDescription.Metadata;
24 import com.android.tradefed.device.ITestDevice;
25 import com.android.tradefed.device.ITestDevice.RecoveryMode;
26 import com.android.tradefed.invoker.proto.InvocationContext.Context;
27 import com.android.tradefed.log.LogUtil.CLog;
28 import com.android.tradefed.testtype.suite.ITestSuite;
29 import com.android.tradefed.util.MultiMap;
30 import com.android.tradefed.util.UniqueMultiMap;
31 
32 import java.io.IOException;
33 import java.io.ObjectInputStream;
34 import java.util.ArrayList;
35 import java.util.LinkedHashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Map.Entry;
39 
40 /**
41  * Generic implementation of a {@link IInvocationContext}.
42  */
43 public class InvocationContext implements IInvocationContext {
44     private static final long serialVersionUID = BuildSerializedVersion.VERSION;
45 
46     // Transient field are not serialized
47     private transient Map<ITestDevice, IBuildInfo> mAllocatedDeviceAndBuildMap;
48     /** Map of the configuration device name and the actual {@link ITestDevice} * */
49     private transient Map<String, ITestDevice> mNameAndDeviceMap;
50     private Map<String, IBuildInfo> mNameAndBuildinfoMap;
51     private final UniqueMultiMap<String, String> mInvocationAttributes =
52             new UniqueMultiMap<String, String>();
53     private Map<IInvocationContext.TimingEvent, Long> mInvocationTimingMetrics;
54     /** Invocation test-tag **/
55     private String mTestTag;
56     /** configuration descriptor */
57     private ConfigurationDescriptor mConfigurationDescriptor;
58     /** module invocation context (when running as part of a {@link ITestSuite} */
59     private IInvocationContext mModuleContext;
60     /**
61      * List of map the device serials involved in the sharded invocation, empty if not a sharded
62      * invocation.
63      */
64     private Map<Integer, List<String>> mShardSerials;
65 
66     private boolean mLocked;
67 
68     /**
69      * Creates a {@link BuildInfo} using default attribute values.
70      */
InvocationContext()71     public InvocationContext() {
72         mInvocationTimingMetrics = new LinkedHashMap<>();
73         mAllocatedDeviceAndBuildMap = new LinkedHashMap<ITestDevice, IBuildInfo>();
74         // Use LinkedHashMap to ensure key ordering by insertion order
75         mNameAndDeviceMap = new LinkedHashMap<String, ITestDevice>();
76         mNameAndBuildinfoMap = new LinkedHashMap<String, IBuildInfo>();
77         mShardSerials = new LinkedHashMap<Integer, List<String>>();
78     }
79 
80     @Override
getInvocationId()81     public String getInvocationId() {
82         List<String> values = mInvocationAttributes.get(INVOCATION_ID);
83         return values == null || values.isEmpty() ? null : values.get(0);
84     }
85 
86     /**
87      * {@inheritDoc}
88      */
89     @Override
getNumDevicesAllocated()90     public int getNumDevicesAllocated() {
91         return mAllocatedDeviceAndBuildMap.size();
92     }
93 
94     /**
95      * {@inheritDoc}
96      */
97     @Override
addAllocatedDevice(String devicename, ITestDevice testDevice)98     public void addAllocatedDevice(String devicename, ITestDevice testDevice) {
99         mNameAndDeviceMap.put(devicename, testDevice);
100         // back fill the information if possible
101         if (mNameAndBuildinfoMap.get(devicename) != null) {
102             mAllocatedDeviceAndBuildMap.put(testDevice, mNameAndBuildinfoMap.get(devicename));
103         }
104     }
105 
106     /**
107      * {@inheritDoc}
108      */
109     @Override
addAllocatedDevice(Map<String, ITestDevice> deviceWithName)110     public void addAllocatedDevice(Map<String, ITestDevice> deviceWithName) {
111         mNameAndDeviceMap.putAll(deviceWithName);
112         // back fill the information if possible
113         for (Entry<String, ITestDevice> entry : deviceWithName.entrySet()) {
114             if (mNameAndBuildinfoMap.get(entry.getKey()) != null) {
115                 mAllocatedDeviceAndBuildMap.put(
116                         entry.getValue(), mNameAndBuildinfoMap.get(entry.getKey()));
117             }
118         }
119     }
120 
121     /**
122      * {@inheritDoc}
123      */
124     @Override
getDeviceBuildMap()125     public Map<ITestDevice, IBuildInfo> getDeviceBuildMap() {
126         return mAllocatedDeviceAndBuildMap;
127     }
128 
129     /**
130      * {@inheritDoc}
131      */
132     @Override
getDevices()133     public List<ITestDevice> getDevices() {
134         return new ArrayList<ITestDevice>(mNameAndDeviceMap.values());
135     }
136 
137     /**
138      * {@inheritDoc}
139      */
140     @Override
getBuildInfos()141     public List<IBuildInfo> getBuildInfos() {
142         return new ArrayList<IBuildInfo>(mNameAndBuildinfoMap.values());
143     }
144 
145     /**
146      * {@inheritDoc}
147      */
148     @Override
getSerials()149     public List<String> getSerials() {
150         List<String> listSerials = new ArrayList<String>();
151         for (ITestDevice testDevice : mNameAndDeviceMap.values()) {
152             listSerials.add(testDevice.getSerialNumber());
153         }
154         return listSerials;
155     }
156 
157     /**
158      * {@inheritDoc}
159      */
160     @Override
getDeviceConfigNames()161     public List<String> getDeviceConfigNames() {
162         List<String> listNames = new ArrayList<String>();
163         listNames.addAll(mNameAndDeviceMap.keySet());
164         return listNames;
165     }
166 
167     /**
168      * {@inheritDoc}
169      */
170     @Override
getDevice(String deviceName)171     public ITestDevice getDevice(String deviceName) {
172         return mNameAndDeviceMap.get(deviceName);
173     }
174 
175     /**
176      * {@inheritDoc}
177      */
178     @Override
getBuildInfo(String deviceName)179     public IBuildInfo getBuildInfo(String deviceName) {
180         return mNameAndBuildinfoMap.get(deviceName);
181     }
182 
183     /**
184      * {@inheritDoc}
185      */
186     @Override
addDeviceBuildInfo(String deviceName, IBuildInfo buildinfo)187     public void addDeviceBuildInfo(String deviceName, IBuildInfo buildinfo) {
188         mNameAndBuildinfoMap.put(deviceName, buildinfo);
189         mAllocatedDeviceAndBuildMap.put(getDevice(deviceName), buildinfo);
190     }
191 
192     /**
193      * {@inheritDoc}
194      */
195     @Override
getBuildInfo(ITestDevice testDevice)196     public IBuildInfo getBuildInfo(ITestDevice testDevice) {
197         return mAllocatedDeviceAndBuildMap.get(testDevice);
198     }
199 
200     /**
201      * {@inheritDoc}
202      */
203     @Override
addInvocationAttribute(String attributeName, String attributeValue)204     public void addInvocationAttribute(String attributeName, String attributeValue) {
205         if (mLocked) {
206             throw new IllegalStateException(
207                     "Attempting to add invocation attribute during a test.");
208         }
209         mInvocationAttributes.put(attributeName, attributeValue);
210     }
211 
212     /** {@inheritDoc} */
213     @Override
addInvocationAttributes(MultiMap<String, String> attributesMap)214     public void addInvocationAttributes(MultiMap<String, String> attributesMap) {
215         if (mLocked) {
216             throw new IllegalStateException(
217                     "Attempting to add invocation attribute during a test.");
218         }
219         mInvocationAttributes.putAll(attributesMap);
220     }
221 
222     /** {@inheritDoc} */
223     @Override
getAttributes()224     public MultiMap<String, String> getAttributes() {
225         // Return a copy of the map to avoid unwanted modifications.
226         UniqueMultiMap<String, String> copy = new UniqueMultiMap<>();
227         copy.putAll(mInvocationAttributes);
228         return copy;
229     }
230 
231 
232     /** {@inheritDoc} */
233     @Override
getInvocationTimingMetrics()234     public Map<IInvocationContext.TimingEvent, Long> getInvocationTimingMetrics() {
235         return mInvocationTimingMetrics;
236     }
237 
238     /**
239      * {@inheritDoc}
240      */
241     @Override
addInvocationTimingMetric(IInvocationContext.TimingEvent timingEvent, Long durationMillis)242     public void addInvocationTimingMetric(IInvocationContext.TimingEvent timingEvent,
243             Long durationMillis) {
244         mInvocationTimingMetrics.put(timingEvent, durationMillis);
245     }
246 
247     /**
248      * {@inheritDoc}
249      */
250     @Override
getDeviceBySerial(String serial)251     public ITestDevice getDeviceBySerial(String serial) {
252         for (ITestDevice testDevice : mNameAndDeviceMap.values()) {
253             if (testDevice.getSerialNumber().equals(serial)) {
254                 return testDevice;
255             }
256         }
257         CLog.d("Device with serial '%s', not found in the metadata", serial);
258         return null;
259     }
260 
261     /** {@inheritDoc} */
262     @Override
getDeviceName(ITestDevice device)263     public String getDeviceName(ITestDevice device) {
264         for (String name : mNameAndDeviceMap.keySet()) {
265             if (device.equals(getDevice(name))) {
266                 return name;
267             }
268         }
269         CLog.d(
270                 "Device with serial '%s' doesn't match a name in the metadata",
271                 device.getSerialNumber());
272         return null;
273     }
274 
275     /** {@inheritDoc} */
276     @Override
getBuildInfoName(IBuildInfo info)277     public String getBuildInfoName(IBuildInfo info) {
278         for (String name : mNameAndBuildinfoMap.keySet()) {
279             if (info.equals(getBuildInfo(name))) {
280                 return name;
281             }
282         }
283         CLog.d("Build info doesn't match a name in the metadata");
284         return null;
285     }
286 
287     /** {@inheritDoc} */
288     @Override
getTestTag()289     public String getTestTag() {
290         return mTestTag;
291     }
292 
293     /**
294      * {@inheritDoc}
295      */
296     @Override
setTestTag(String testTag)297     public void setTestTag(String testTag) {
298         mTestTag = testTag;
299     }
300 
301     /**
302      * {@inheritDoc}
303      */
304     @Override
setRecoveryModeForAllDevices(RecoveryMode mode)305     public void setRecoveryModeForAllDevices(RecoveryMode mode) {
306         for (ITestDevice device : getDevices()) {
307             device.setRecoveryMode(mode);
308         }
309     }
310 
311     /** {@inheritDoc} */
312     @Override
setConfigurationDescriptor(ConfigurationDescriptor configurationDescriptor)313     public void setConfigurationDescriptor(ConfigurationDescriptor configurationDescriptor) {
314         mConfigurationDescriptor = configurationDescriptor;
315     }
316 
317     /** {@inheritDoc} */
318     @Override
getConfigurationDescriptor()319     public ConfigurationDescriptor getConfigurationDescriptor() {
320         return mConfigurationDescriptor;
321     }
322 
323     /** {@inheritDoc} */
324     @Override
setModuleInvocationContext(IInvocationContext invocationContext)325     public void setModuleInvocationContext(IInvocationContext invocationContext) {
326         mModuleContext = invocationContext;
327     }
328 
329     /** {@inheritDoc} */
330     @Override
getModuleInvocationContext()331     public IInvocationContext getModuleInvocationContext() {
332         return mModuleContext;
333     }
334 
335     /** Lock the context to prevent more invocation attributes to be added. */
lockAttributes()336     public void lockAttributes() {
337         mLocked = true;
338     }
339 
340     /** Private method to unlock the attributes. Used for sandbox test mode only. */
341     @SuppressWarnings("unused")
unlock()342     private void unlock() {
343         mLocked = false;
344     }
345 
346     /** {@inheritDoc} */
347     @Override
addSerialsFromShard(Integer index, List<String> serials)348     public void addSerialsFromShard(Integer index, List<String> serials) {
349         if (mLocked) {
350             throw new IllegalStateException(
351                     "Attempting to add serial from shard attribute during a test.");
352         }
353         mShardSerials.put(index, serials);
354     }
355 
356     /** {@inheritDoc} */
357     @Override
getShardsSerials()358     public Map<Integer, List<String>> getShardsSerials() {
359         return new LinkedHashMap<>(mShardSerials);
360     }
361 
362     /** Special java method that allows for custom deserialization. */
readObject(ObjectInputStream in)363     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
364         // our "pseudo-constructor"
365         in.defaultReadObject();
366         // now we are a "live" object again, so let's init the transient field
367         mAllocatedDeviceAndBuildMap = new LinkedHashMap<ITestDevice, IBuildInfo>();
368         mNameAndDeviceMap = new LinkedHashMap<String, ITestDevice>();
369         // For compatibility, when parent TF does not have the invocation timing yet.
370         if (mInvocationTimingMetrics == null) {
371             mInvocationTimingMetrics = new LinkedHashMap<>();
372         }
373     }
374 
375     /** {@inheritDoc} */
376     @Override
toProto()377     public Context toProto() {
378         Context.Builder contextBuilder = Context.newBuilder();
379         // The invocation test tag.
380         if (mTestTag != null) {
381             contextBuilder.setTestTag(mTestTag);
382         }
383         // Map name to build info
384         Map<String, BuildInformation.BuildInfo> mapBuild = new LinkedHashMap<>();
385         for (String name : mNameAndBuildinfoMap.keySet()) {
386             mapBuild.put(name, mNameAndBuildinfoMap.get(name).toProto());
387         }
388         contextBuilder.putAllNameBuildInfo(mapBuild);
389         // Metadata
390         List<Metadata> metadatas = new ArrayList<>();
391         for (String key : mInvocationAttributes.keySet()) {
392             Metadata value =
393                     Metadata.newBuilder()
394                             .setKey(key)
395                             .addAllValue(mInvocationAttributes.get(key))
396                             .build();
397             metadatas.add(value);
398         }
399         contextBuilder.addAllMetadata(metadatas);
400         // Configuration Description
401         contextBuilder.setConfigurationDescription(mConfigurationDescriptor.toProto());
402         // Module Context if it exists
403         if (mModuleContext != null) {
404             contextBuilder.setModuleContext(mModuleContext.toProto());
405         }
406         return contextBuilder.build();
407     }
408 
409     /** Inverse operation to {@link InvocationContext#toProto()} to get the instance back. */
fromProto(Context protoContext)410     public static InvocationContext fromProto(Context protoContext) {
411         InvocationContext context = new InvocationContext();
412         // Test Tag.
413         context.mTestTag = protoContext.getTestTag();
414         // Map Build Info
415         for (String key : protoContext.getNameBuildInfo().keySet()) {
416             context.mNameAndBuildinfoMap.put(
417                     key, BuildInfo.fromProto(protoContext.getNameBuildInfo().get(key)));
418         }
419         // Metadata
420         for (Metadata meta : protoContext.getMetadataList()) {
421             for (String value : meta.getValueList()) {
422                 context.mInvocationAttributes.put(meta.getKey(), value);
423             }
424         }
425         // Configuration Description
426         context.mConfigurationDescriptor =
427                 ConfigurationDescriptor.fromProto(protoContext.getConfigurationDescription());
428         // Module Context - context module will have some property set: module-id at the minimum
429         if (protoContext.hasModuleContext()) {
430             // TODO: Check explicitly for module-id
431             context.mModuleContext = InvocationContext.fromProto(protoContext.getModuleContext());
432         }
433         return context;
434     }
435 }
436