feat(mdtest auto): add more options to group devices
mdtest supports --groupby option which allows users to provide grouping
keyword for mdtest to group devices and run tests under different device
settings based on app-device coverage. --groupby supports options like
'device-id', 'model-name', 'os-version', 'api-level' and 'screen-size'.
Fix minor bug for mdtest script to deal with the first run.
Change-Id: I54a6b91fcd40d995e6413b3d556c3c1fc526c6a5
diff --git a/mdtest/bin/mdtest b/mdtest/bin/mdtest
index 3c2f60f..574e1f4 100755
--- a/mdtest/bin/mdtest
+++ b/mdtest/bin/mdtest
@@ -38,6 +38,8 @@
exit 0
fi
+mkdir -p "$MDTEST_TOOL/bin/cache"
+
REVISION=`(cd "$MDTEST_TOOL"; git rev-parse HEAD)`
if [ ! -f "$SNAPSHOT_PATH" ] || [ ! -f "$STAMP_PATH" ] || [ `cat "$STAMP_PATH"` != "$REVISION" ] || [ "$MDTEST_TOOL/pubspec.yaml" -nt "$MDTEST_TOOL/pubspec.lock" ]; then
echo Building mdtest ...
diff --git a/mdtest/lib/src/algorithms/coverage.dart b/mdtest/lib/src/algorithms/coverage.dart
index 6791af3..32e79bc 100644
--- a/mdtest/lib/src/algorithms/coverage.dart
+++ b/mdtest/lib/src/algorithms/coverage.dart
@@ -8,13 +8,13 @@
import '../mobile/device_spec.dart' show DeviceSpec;
import '../util.dart';
-class ClusterInfo {
+class GroupInfo {
Map<String, List<Device>> _deviceClusters;
Map<String, List<DeviceSpec>> _deviceSpecClusters;
List<String> _deviceClustersOrder;
List<String> _deviceSpecClustersOrder;
- ClusterInfo(
+ GroupInfo(
Map<String, List<Device>> deviceClusters,
Map<String, List<DeviceSpec>> deviceSpecClusters
) {
@@ -39,7 +39,7 @@
}
}
- ClusterInfo clusterInfo;
+ GroupInfo clusterInfo;
// Coverage matrix, where a row indicats an app cluster and a column
// indicates a device cluster
List<List<int>> matrix;
@@ -47,9 +47,9 @@
void fill(Map<DeviceSpec, Device> match) {
match.forEach((DeviceSpec spec, Device device) {
int rowNum = clusterInfo.deviceSpecClustersOrder
- .indexOf(spec.clusterKey());
+ .indexOf(spec.groupKey());
int colNum = clusterInfo.deviceClustersOrder
- .indexOf(device.clusterKey());
+ .indexOf(device.groupKey());
matrix[rowNum][colNum] = 1;
});
}
@@ -78,7 +78,7 @@
Map<CoverageMatrix, Map<DeviceSpec, Device>> buildCoverage2MatchMapping(
List<Map<DeviceSpec, Device>> allMatches,
- ClusterInfo clusterInfo
+ GroupInfo clusterInfo
) {
Map<CoverageMatrix, Map<DeviceSpec, Device>> cov2match
= <CoverageMatrix, Map<DeviceSpec, Device>>{};
@@ -97,7 +97,7 @@
/// [ref link]: https://en.wikipedia.org/wiki/Set_cover_problem
Set<Map<DeviceSpec, Device>> findMinimumMappings(
Map<CoverageMatrix, Map<DeviceSpec, Device>> cov2match,
- ClusterInfo clusterInfo
+ GroupInfo clusterInfo
) {
Set<CoverageMatrix> minSet = new Set<CoverageMatrix>();
CoverageMatrix base = new CoverageMatrix(clusterInfo);
diff --git a/mdtest/lib/src/algorithms/matching.dart b/mdtest/lib/src/algorithms/matching.dart
index 5f97fc8..447cfea 100644
--- a/mdtest/lib/src/algorithms/matching.dart
+++ b/mdtest/lib/src/algorithms/matching.dart
@@ -142,9 +142,9 @@
for (Map<DeviceSpec, Device> match in matches) {
sb.writeln('Round $roundNum:');
match.forEach((DeviceSpec spec, Device device) {
- sb.writeln('[Spec Cluster Key: ${spec.clusterKey()}]'
+ sb.writeln('[Spec Cluster Key: ${spec.groupKey()}]'
' -> '
- '[Device Cluster Key: ${device.clusterKey()}]');
+ '[Device Cluster Key: ${device.groupKey()}]');
});
roundNum++;
}
diff --git a/mdtest/lib/src/commands/auto.dart b/mdtest/lib/src/commands/auto.dart
index 088d8c2..1fe236d 100644
--- a/mdtest/lib/src/commands/auto.dart
+++ b/mdtest/lib/src/commands/auto.dart
@@ -33,12 +33,12 @@
printInfo('Running "mdtest auto command" ...');
this._specs = await loadSpecs(argResults);
- if (sanityCheckSpecs(_specs, argResults['spec']) != 0) {
+ if (sanityCheckSpecs(_specs, argResults['specs']) != 0) {
printError('Test spec does not meet requirements.');
return 1;
}
- this._devices = await getDevices();
+ this._devices = await getDevices(groupKey: argResults['groupby']);
if (_devices.isEmpty) {
printError('No device found.');
return 1;
@@ -59,7 +59,7 @@
Map<String, List<DeviceSpec>> deviceSpecClusters
= buildCluster(allDeviceSpecs);
- ClusterInfo clusterInfo = new ClusterInfo(deviceClusters, deviceSpecClusters);
+ GroupInfo clusterInfo = new GroupInfo(deviceClusters, deviceSpecClusters);
Map<CoverageMatrix, Map<DeviceSpec, Device>> cov2match
= buildCoverage2MatchMapping(allDeviceMappings, clusterInfo);
Set<Map<DeviceSpec, Device>> chosenMappings
@@ -132,5 +132,11 @@
usesSpecsOption();
usesCoverageFlag();
usesTAPReportOption();
+ argParser.addOption('groupby',
+ defaultsTo: 'device-id',
+ allowed: ['device-id', 'model-name', 'os-version', 'api-level', 'screen-size'],
+ help: 'Device property used to group devices to'
+ 'adjust app-device coverage criterion.'
+ );
}
}
diff --git a/mdtest/lib/src/mobile/device.dart b/mdtest/lib/src/mobile/device.dart
index 6629eec..c69fd74 100644
--- a/mdtest/lib/src/mobile/device.dart
+++ b/mdtest/lib/src/mobile/device.dart
@@ -8,32 +8,40 @@
import 'key_provider.dart';
import 'android.dart';
-class Device implements ClusterKeyProvider {
+class Device implements GroupKeyProvider {
Device({
- this.properties
- });
+ this.properties,
+ String groupKey
+ }) {
+ this._groupKey = groupKey;
+ }
Map<String, String> properties;
+ String _groupKey;
String get id => properties['device-id'];
String get modelName => properties['model-name'];
String get screenSize => properties['screen-size'];
+ String get osVersion => properties['os-version'];
+ String get apiLevel => properties['api-level'];
+ /// default to 'device-id'
@override
- String clusterKey() {
- return id;
+ String groupKey() {
+ return properties[_groupKey ?? 'device-id'];
}
@override
String toString()
- => '<id: $id, model-name: $modelName, screen-size: $screenSize>';
+ => '<device-id: $id, model-name: $modelName, screen-size: $screenSize, '
+ 'os-version: $osVersion, api-level: $apiLevel>';
}
-Future<List<Device>> getDevices() async {
+Future<List<Device>> getDevices({String groupKey}) async {
List<Device> devices = <Device>[];
await _getDeviceIDs().then((List<String> ids) async {
for(String id in ids) {
- devices.add(await _collectDeviceProps(id));
+ devices.add(await _collectDeviceProps(id, groupKey: groupKey));
}
});
return devices;
@@ -73,12 +81,15 @@
return deviceIDs;
}
-Future<Device> _collectDeviceProps(String deviceID) async {
+Future<Device> _collectDeviceProps(String deviceID, {String groupKey}) async {
return new Device(
properties: <String, String> {
'device-id': deviceID,
'model-name': await getProperty(deviceID, 'ro.product.model'),
+ 'os-version': await getProperty(deviceID, 'ro.build.version.release'),
+ 'api-level': await getProperty(deviceID, 'ro.build.version.sdk'),
'screen-size': await getScreenSize(deviceID)
- }
+ },
+ groupKey: groupKey
);
}
diff --git a/mdtest/lib/src/mobile/device_spec.dart b/mdtest/lib/src/mobile/device_spec.dart
index 2f7e924..77f604f 100644
--- a/mdtest/lib/src/mobile/device_spec.dart
+++ b/mdtest/lib/src/mobile/device_spec.dart
@@ -13,7 +13,7 @@
import '../globals.dart';
import '../util.dart';
-class DeviceSpec implements ClusterKeyProvider {
+class DeviceSpec implements GroupKeyProvider {
DeviceSpec(String nickname, { this.specProperties }) {
specProperties['nickname'] = nickname;
}
@@ -51,7 +51,7 @@
}
@override
- String clusterKey() {
+ String groupKey() {
return appPath;
}
diff --git a/mdtest/lib/src/mobile/key_provider.dart b/mdtest/lib/src/mobile/key_provider.dart
index 3974539..d0a9dc1 100644
--- a/mdtest/lib/src/mobile/key_provider.dart
+++ b/mdtest/lib/src/mobile/key_provider.dart
@@ -2,14 +2,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-abstract class ClusterKeyProvider {
- String clusterKey();
+abstract class GroupKeyProvider {
+ /// key to group devices or specs
+ String groupKey();
}
Map<String, List<dynamic>> buildCluster(List<dynamic> elements) {
Map<String, List<dynamic>> clusters = <String, List<dynamic>>{};
elements.forEach((dynamic element) {
- clusters.putIfAbsent(element.clusterKey(), () => <dynamic>[])
+ clusters.putIfAbsent(element.groupKey(), () => <dynamic>[])
.add(element);
});
return clusters;