Skip to content

Commit

Permalink
Refactored for deviceFarmCmd
Browse files Browse the repository at this point in the history
  • Loading branch information
mmcc007 committed Mar 17, 2019
1 parent b269167 commit 76e4eb1
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 64 deletions.
103 changes: 43 additions & 60 deletions lib/sylph.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import 'package:yaml/yaml.dart';

enum DeviceType { ios, android }

const resourcesUri = 'package:sylph/resources';
const appiumTemplate = 'appium_template.zip';
const testBundle = 'test_bundle.zip';
const kResourcesUri = 'package:sylph/resources';
const kAppiumTemplate = 'appium_template.zip';
const kTestBundle = 'test_bundle.zip';

/// parse a yaml file to a map
Future<Map> parseYaml(String filePath) async {
Expand All @@ -23,50 +23,44 @@ Future<Map> parseYaml(String filePath) async {
/// Creates new project if none exists.
String setupProject(String projectName, int jobTimeoutMinutes) {
// check for existing project
final List projectList =
jsonDecode(cmd('aws', ['devicefarm', 'list-projects']))['projects'];
Map result = projectList.firstWhere(
(project) => project['name'] == projectName,
final projects = deviceFarmCmd(['list-projects'])['projects'];
Map result = projects.firstWhere((project) => project['name'] == projectName,
orElse: () => null);

if (result == null) {
// create project
print('Creating project for $projectName ...');
result = jsonDecode(cmd('aws', [
'devicefarm',
return deviceFarmCmd([
'create-project',
'--name',
projectName,
'--default-job-timeout-minutes',
'$jobTimeoutMinutes'
]));
return result['project']['arn'];
])['project']['arn'];
} else
return result['arn'];
}

/// Set up a device pool if named pool does not exist.
String setupDevicePool(String projectArn, String poolName, List devices) {
// check for existing pool
final List poolList = jsonDecode(cmd('aws', [
'devicefarm',
final pools = deviceFarmCmd([
'list-device-pools',
'--arn',
projectArn,
'--type',
'PRIVATE'
]))['devicePools'];
Map result = poolList.firstWhere((pool) => pool['name'] == poolName,
orElse: () => null);
])['devicePools'];
final pool =
pools.firstWhere((pool) => pool['name'] == poolName, orElse: () => null);

if (result == null) {
// create device pool
if (pool == null) {
// create new device pool
print('Creating device pool $poolName ...');
// convert devices to rules
List rules = deviceSpecToRules(devices);

result = jsonDecode(cmd('aws', [
'devicefarm',
final newPool = deviceFarmCmd([
'create-device-pool',
'--name',
poolName,
Expand All @@ -76,19 +70,18 @@ String setupDevicePool(String projectArn, String poolName, List devices) {
jsonEncode(rules),
// number of devices in pool should not exceed number of devices requested
// '--max-devices', '${devices.length}'
]));
return result['devicePool']['arn'];
])['devicePool'];
return newPool['arn'];
} else
return result['arn'];
return pool['arn'];
}

/// Schedules a run.
String scheduleRun(String runName, String projectArn, String appArn,
String devicePoolArn, String testSpecArn, String testPackageArn) {
// Schedule run
print('Starting $runName on AWS Device Farms');
String runArn = jsonDecode(cmd('aws', [
'devicefarm',
return deviceFarmCmd([
'schedule-run',
'--project-arn',
projectArn,
Expand All @@ -102,37 +95,34 @@ String scheduleRun(String runName, String projectArn, String appArn,
'testSpecArn=$testSpecArn,type=APPIUM_PYTHON,testPackageArn=$testPackageArn',
// '--execution-configuration',
// 'jobTimeoutMinutes=5,accountsCleanup=false,appPackagesCleanup=false,videoCapture=true,skipAppResign=true'
]))['run']['arn'];
return runArn;
])['run']['arn'];
}

/// Tracks run status.
/// Tracks run status and returns final run.
Map runStatus(String runArn, int timeout) {
Map result;
Map run;
for (int i = 0; i < timeout; i++) {
result = jsonDecode(cmd('aws', [
'devicefarm',
final run = deviceFarmCmd([
'get-run',
'--arn',
runArn,
]));
sleep(Duration(seconds: 2));
final status = result['run']['status'];
])['run'];
final runStatus = run['status'];

// print run status
print('Run status: $status');
print('Run status: $runStatus');

if (status == 'COMPLETED')
if (runStatus == 'COMPLETED')
break;
else if (i == timeout - 2) throw 'Error: run timed-out';
sleep(Duration(seconds: 2));
}
return result;
return run;
}

/// Run report.
void runReport(Map result) {
void runReport(Map run) {
// generate report
final run = result['run'];
print('run=$run');
print(
'Run \'${run['name']}\' completed in ${run['deviceMinutes']['total']} minutes.');
Expand All @@ -149,17 +139,17 @@ void runReport(Map result) {
/// Finds the ARN of a device.
String findDeviceArn(String name, String model, String os) {
assert(name != null && model != null && os != null);
final List deviceList = jsonDecode(cmd('aws', [
final devices = deviceFarmCmd([
'devicefarm',
'list-devices',
]))['devices'];
Map result = deviceList.firstWhere(
])['devices'];
Map device = devices.firstWhere(
(device) => (device['name'] == name &&
device['modelId'] == model &&
device['os'] == os),
orElse: () =>
throw 'Error: device does not exist: name=$name, model=$model, os=$os');
return result['arn'];
return device['arn'];
}

/// Converts a list of devices to a list of rules.
Expand All @@ -183,8 +173,7 @@ List deviceSpecToRules(List devices) {
/// Returns file ARN.
String uploadFile(String projectArn, String filePath, String fileType) {
// 1. Create upload
String result = cmd('aws', [
'devicefarm',
final uploadRequested = deviceFarmCmd([
'create-upload',
'--project-arn',
projectArn,
Expand All @@ -193,19 +182,18 @@ String uploadFile(String projectArn, String filePath, String fileType) {
'--type',
fileType
]);
Map resultMap = jsonDecode(result);
final fileUploadUrl = resultMap['upload']['url'];
final fileUploadArn = resultMap['upload']['arn'];
final fileUploadUrl = uploadRequested['upload']['url'];
final fileUploadArn = uploadRequested['upload']['arn'];

// 2. Upload file
result = cmd('curl', ['-T', filePath, fileUploadUrl]);
cmd('curl', ['-T', filePath, fileUploadUrl]);

// 3. Wait until file upload complete
for (int i = 0; i < 5; i++) {
result = cmd('aws', ['devicefarm', 'get-upload', '--arn', fileUploadArn]);
final upload =
deviceFarmCmd(['get-upload', '--arn', fileUploadArn])['upload'];
sleep(Duration(seconds: 1));
resultMap = jsonDecode(result);
final status = resultMap['upload']['status'];
final status = upload['status'];
if (status == 'SUCCEEDED')
break;
else if (i == 4)
Expand All @@ -219,7 +207,7 @@ String uploadFile(String projectArn, String filePath, String fileType) {
Future bundleFlutterTests(Map config) async {
final tmpDir = config['tmp_dir'];
clearDirectory(tmpDir);
final testBundlePath = '$tmpDir/$testBundle';
final testBundlePath = '$tmpDir/$kTestBundle';
await unpackResources(tmpDir);

// final testSuite = config['test_suites'][0];
Expand Down Expand Up @@ -248,18 +236,13 @@ Future bundleFlutterTests(Map config) async {
/// Downloads artifacts generated by a run.
void downloadArtifacts(String runArn, String downloadDir) {
clearDirectory(downloadDir);
var raw = cmd('aws',
['devicefarm', 'list-artifacts', '--arn', runArn, '--type', 'FILE']);
// print('raw=$raw');
var artifacts = jsonDecode(raw)['artifacts'];
var artifacts = deviceFarmCmd(
['list-artifacts', '--arn', runArn, '--type', 'FILE'])['artifacts'];

for (var artifact in artifacts) {
// print('artifact=$artifact');
final name = artifact['name'];
final extension = artifact['extension'];
// final type = artifact['type'];
final fileUrl = artifact['url'];

final fileName =
name.replaceAll(' ', '_') + '.' + Uuid().v1() + '.' + extension;
final filePath = downloadDir + '/' + fileName;
Expand Down
17 changes: 13 additions & 4 deletions lib/utils.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';

import 'package:sylph/sylph.dart';
import 'dart:io';
Expand All @@ -16,7 +17,7 @@ void clearDirectory(String dir) {

/// Read a file image from resources.
Future<List<int>> readResourceImage(String fileImageName) async {
final resource = Resource('$resourcesUri/$fileImageName');
final resource = Resource('$kResourcesUri/$fileImageName');
return resource.readAsBytes();
}

Expand All @@ -41,6 +42,13 @@ String cmd(String cmd, List<String> arguments,
return result.stdout;
}

/// Runs a device farm command and returns as Map
Map deviceFarmCmd(List<String> arguments,
[String workingDir = '.', bool silent = true]) {
return jsonDecode(
cmd('aws', ['devicefarm']..addAll(arguments), workingDir, silent));
}

/// Converts [DeviceType] to [String]
String deviceTypeStr(DeviceType deviceType) {
return DeviceType.ios.toString().split('.')[1];
Expand All @@ -57,10 +65,11 @@ Map getDevicePoolInfo(Map config, String poolName) {
}

Future unpackResources(String tmpDir) async {
final testBundlePath = '$tmpDir/$testBundle';
final testBundlePath = '$tmpDir/$kTestBundle';

// unpack Appium template
await writeFileImage(await readResourceImage(appiumTemplate), testBundlePath);
await writeFileImage(
await readResourceImage(kAppiumTemplate), testBundlePath);

// unpack scripts
final appPath = Directory.current.path;
Expand All @@ -83,7 +92,7 @@ Future<void> unpackScripts(String dstDir) async {

/// Read script from resources and install in staging area.
Future unpackScript(String srcPath, String dstDir) async {
final resource = Resource('$resourcesUri/$srcPath');
final resource = Resource('$kResourcesUri/$srcPath');
final String script = await resource.readAsString();
final file = await File('$dstDir/$srcPath').create(recursive: true);
await file.writeAsString(script, flush: true);
Expand Down
11 changes: 11 additions & 0 deletions test/sylph_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,15 @@ void main() {
// list artifacts
downloadArtifacts(runArn, downloadDir);
});

test('run device farm command', () {
final projectName = 'flutter tests';
var projectInfo = deviceFarmCmd(['list-projects']);
final projects = projectInfo['projects'];
final project = projects.firstWhere(
(project) => project['name'] == projectName,
orElse: () => null);
print(project);
expect(project['name'], projectName);
});
}

0 comments on commit 76e4eb1

Please sign in to comment.