| // Copyright 2016 The Vanadium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| import 'dart:async'; |
| import 'dart:convert'; |
| import 'dart:io'; |
| |
| import 'package:args/args.dart'; |
| |
| import 'device.dart'; |
| import 'key_provider.dart'; |
| import '../globals.dart'; |
| import '../util.dart'; |
| |
| class DeviceSpec implements GroupKeyProvider { |
| DeviceSpec(String nickname, { this.specProperties }) { |
| specProperties['nickname'] = nickname; |
| } |
| |
| Map<String, String> specProperties; |
| |
| String get nickName => specProperties['nickname']; |
| String get deviceID => specProperties['device-id']; |
| String get deviceModelName => specProperties['model-name']; |
| String get deviceOSVersion => specProperties['os-version']; |
| String get deviceAPILevel => specProperties['api-level']; |
| String get deviceScreenSize => specProperties['screen-size']; |
| String get appRootPath => specProperties['app-root']; |
| String get appPath => specProperties['app-path']; |
| String get observatoryUrl => specProperties['observatory-url']; |
| void set observatoryUrl(String url) { |
| specProperties['observatory-url'] = url; |
| } |
| |
| /// Match if property names are not specified or equal to the device property. |
| /// Checked property names includes: device-id, model-name, screen-size |
| bool matches(Device device) { |
| List<String> checkedProperties = [ |
| 'device-id', |
| 'model-name', |
| 'os-version', |
| 'api-level', |
| 'screen-size' |
| ]; |
| return checkedProperties.every( |
| (String propertyName) => isNullOrEqual(propertyName, device) |
| ); |
| } |
| |
| bool isNullOrEqual(String propertyName, Device device) { |
| return specProperties[propertyName] == null |
| || |
| specProperties[propertyName] == device.properties[propertyName]; |
| } |
| |
| @override |
| String groupKey() { |
| return appPath; |
| } |
| |
| @override |
| String toString() => '<nickname: $nickName, ' |
| 'id: $deviceID, ' |
| 'model name: $deviceModelName, ' |
| 'os version: $deviceOSVersion, ' |
| 'api level: $deviceAPILevel, ' |
| 'screen size: $deviceScreenSize, ' |
| 'port: $observatoryUrl, ' |
| 'app path: $appPath>'; |
| } |
| |
| Future<dynamic> loadSpecs(ArgResults argResults) async { |
| String specsPath = argResults['spec']; |
| try { |
| // Read specs file into json format |
| dynamic newSpecs = JSON.decode(await new File(specsPath).readAsString()); |
| // Get the parent directory of the specs file |
| String rootPath = new File(specsPath).parent.absolute.path; |
| // Normalize the 'test-path' specified from the command line argument |
| List<String> testPathsFromCommandLine |
| = listFilePathsFromGlobPatterns(Directory.current.path, argResults.rest); |
| printTrace('Test paths from command line: $testPathsFromCommandLine'); |
| newSpecs['test-paths'] = testPathsFromCommandLine; |
| // Normalize the 'app-path' in the specs file |
| newSpecs['devices']?.forEach((String name, Map<String, String> map) { |
| map['app-root'] = normalizePath(rootPath, map['app-root']); |
| map['app-path'] = normalizePath(map['app-root'], map['app-path']); |
| }); |
| return newSpecs; |
| } on FileSystemException { |
| printError('File $specsPath does not exist.'); |
| exit(1); |
| } on FormatException { |
| printError('File $specsPath is not in JSON format.'); |
| exit(1); |
| } catch (e) { |
| printError('Unknown Exception details:\n $e'); |
| exit(1); |
| } |
| } |
| |
| /// Check if test spec meets the requirements. If user does not specify any |
| /// valid test paths neither from the test spec nor from the command line, |
| /// report error. If 'devices' property is not specified, report error. If |
| /// no device spec is specified, report error. If screen size property is not |
| /// one of 'small', 'normal', 'large' and 'xlarge', report error. If app-root |
| /// is not specified or is not a directory, report error. If appPath is not |
| /// specified or is not a file, report error. |
| /// |
| /// Note: If a test path does not exist, it will be ignored and thus does not |
| /// count as a valid test path. If device nickname is not unique, json decoder |
| /// will overwrite the previous device spec associated with the same nickname, |
| /// thus only the last nickname to device spec pair is used. |
| int sanityCheckSpecs(dynamic spec, String specsPath) { |
| if (spec['test-paths'].isEmpty) { |
| printError( |
| 'No test paths found. ' |
| 'You must specify at least one test path.' |
| ); |
| return 1; |
| } |
| dynamic deviceSpecs = spec['devices']; |
| if (deviceSpecs == null) { |
| printError('"devices" property is not specified in $specsPath'); |
| return 1; |
| } |
| if (deviceSpecs.isEmpty) { |
| printError('No device spec is found in $specsPath'); |
| return 1; |
| } |
| for (String nickname in deviceSpecs.keys) { |
| dynamic individualDeviceSpec = deviceSpecs[nickname]; |
| List<String> screenSizes = <String>['small', 'normal', 'large', 'xlarge']; |
| if (individualDeviceSpec['screen-size'] != null |
| && |
| !screenSizes.contains(individualDeviceSpec['screen-size'])) { |
| printError('Screen size must be one of $screenSizes'); |
| return 1; |
| } |
| String appRootPath = individualDeviceSpec['app-root']; |
| if (appRootPath == null) { |
| printError('Application root path is not specified.'); |
| return 1; |
| } |
| if (!FileSystemEntity.isDirectorySync(appRootPath)) { |
| printError('Application root path is not a directory.'); |
| return 1; |
| } |
| String appPath = individualDeviceSpec['app-path']; |
| if (appPath == null) { |
| printError('Application path is not specified.'); |
| return 1; |
| } |
| if (!FileSystemEntity.isFileSync(appPath)) { |
| printError('Application path is not a file.'); |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| /// Build a list of device specs from mappings loaded from JSON .spec file |
| Future<List<DeviceSpec>> constructAllDeviceSpecs(dynamic allSpecs) async { |
| List<DeviceSpec> deviceSpecs = <DeviceSpec>[]; |
| for(String name in allSpecs.keys) { |
| Map<String, String> spec = allSpecs[name]; |
| deviceSpecs.add( |
| new DeviceSpec( |
| name, |
| specProperties: spec |
| ) |
| ); |
| } |
| return deviceSpecs; |
| } |