Unverified Commit ce3ce20b authored by Casey Hillers's avatar Casey Hillers Committed by GitHub

[devicelab] Upload git branch (#68541)

parent d2314ecf
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:convert' show json; import 'dart:convert' show Encoding, json;
import 'dart:io'; import 'dart:io';
import 'package:file/file.dart'; import 'package:file/file.dart';
...@@ -15,6 +15,17 @@ import 'package:meta/meta.dart'; ...@@ -15,6 +15,17 @@ import 'package:meta/meta.dart';
import 'task_result.dart'; import 'task_result.dart';
import 'utils.dart'; import 'utils.dart';
typedef ProcessRunSync = ProcessResult Function(
String,
List<String>, {
Map<String, String> environment,
bool includeParentEnvironment,
bool runInShell,
Encoding stderrEncoding,
Encoding stdoutEncoding,
String workingDirectory,
});
/// Class for test runner to interact with Flutter's infrastructure service, Cocoon. /// Class for test runner to interact with Flutter's infrastructure service, Cocoon.
/// ///
/// Cocoon assigns bots to run these devicelab tasks on real devices. /// Cocoon assigns bots to run these devicelab tasks on real devices.
...@@ -24,29 +35,43 @@ class Cocoon { ...@@ -24,29 +35,43 @@ class Cocoon {
String serviceAccountTokenPath, String serviceAccountTokenPath,
@visibleForTesting Client httpClient, @visibleForTesting Client httpClient,
@visibleForTesting FileSystem filesystem, @visibleForTesting FileSystem filesystem,
@visibleForTesting this.processRunSync = Process.runSync,
}) : _httpClient = AuthenticatedCocoonClient(serviceAccountTokenPath, httpClient: httpClient, filesystem: filesystem); }) : _httpClient = AuthenticatedCocoonClient(serviceAccountTokenPath, httpClient: httpClient, filesystem: filesystem);
/// Client to make http requests to Cocoon. /// Client to make http requests to Cocoon.
final AuthenticatedCocoonClient _httpClient; final AuthenticatedCocoonClient _httpClient;
final ProcessRunSync processRunSync;
/// Url used to send results to. /// Url used to send results to.
static const String baseCocoonApiUrl = 'https://flutter-dashboard.appspot.com/api'; static const String baseCocoonApiUrl = 'https://flutter-dashboard.appspot.com/api';
static final Logger logger = Logger('CocoonClient'); static final Logger logger = Logger('CocoonClient');
String get commitBranch => _commitBranch ?? _readCommitBranch();
String _commitBranch;
String get commitSha => _commitSha ?? _readCommitSha(); String get commitSha => _commitSha ?? _readCommitSha();
String _commitSha; String _commitSha;
/// Parse the local repo for the current running commit. /// Parse the local repo for the current running commit.
String _readCommitSha() { String _readCommitSha() {
final ProcessResult result = Process.runSync('git', <String>['rev-parse', 'HEAD']); final ProcessResult result = processRunSync('git', <String>['rev-parse', 'HEAD']);
if (result.exitCode != 0) {
throw CocoonException(result.stderr as String);
}
return _commitSha = result.stdout as String;
}
/// Parse the local repo for the current running branch.
String _readCommitBranch() {
final ProcessResult result = processRunSync('git', <String>['rev-parse', '--abbrev-ref', 'HEAD']);
if (result.exitCode != 0) { if (result.exitCode != 0) {
throw Exception(result.stderr); throw CocoonException(result.stderr as String);
} }
_commitSha = result.stdout as String; return _commitBranch = result.stdout as String;
return _commitSha;
} }
/// Send [TaskResult] to Cocoon. /// Send [TaskResult] to Cocoon.
...@@ -58,6 +83,7 @@ class Cocoon { ...@@ -58,6 +83,7 @@ class Cocoon {
}); });
final Map<String, dynamic> status = <String, dynamic>{ final Map<String, dynamic> status = <String, dynamic>{
'CommitBranch': commitBranch,
'CommitSha': commitSha, 'CommitSha': commitSha,
'TaskName': taskName, 'TaskName': taskName,
'NewStatus': result.succeeded ? 'Succeeded' : 'Failed', 'NewStatus': result.succeeded ? 'Succeeded' : 'Failed',
...@@ -150,3 +176,13 @@ class AuthenticatedCocoonClient extends BaseClient { ...@@ -150,3 +176,13 @@ class AuthenticatedCocoonClient extends BaseClient {
return response; return response;
} }
} }
class CocoonException implements Exception {
CocoonException(this.message) : assert(message != null);
/// The message to show to the issuer to explain the error.
final String message;
@override
String toString() => 'CocoonException: $message';
}
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:convert';
import 'dart:io';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
...@@ -13,21 +16,72 @@ import 'package:flutter_devicelab/framework/task_result.dart'; ...@@ -13,21 +16,72 @@ import 'package:flutter_devicelab/framework/task_result.dart';
import 'common.dart'; import 'common.dart';
void main() { void main() {
group('Cocoon', () { ProcessResult _processResult;
const String serviceAccountTokenPath = 'test_account_file'; ProcessResult runSyncStub(String executable, List<String> args,
const String serviceAccountToken = 'test_token'; {Map<String, String> environment,
bool includeParentEnvironment,
bool runInShell,
Encoding stderrEncoding,
Encoding stdoutEncoding,
String workingDirectory}) =>
_processResult;
// Expected test values.
const String commitBranch = 'flutter-1.23-candidate.18';
const String commitSha = 'a4952838bf288a81d8ea11edfd4b4cd649fa94cc';
const String serviceAccountTokenPath = 'test_account_file';
const String serviceAccountToken = 'test_token';
group('Cocoon', () {
Client mockClient; Client mockClient;
Cocoon cocoon; Cocoon cocoon;
FileSystem fs; FileSystem fs;
setUp(() { setUp(() {
fs = MemoryFileSystem(); fs = MemoryFileSystem();
mockClient = MockClient((Request request) async => Response('{}', 200));
final File serviceAccountFile = fs.file(serviceAccountTokenPath)..createSync(); final File serviceAccountFile = fs.file(serviceAccountTokenPath)..createSync();
serviceAccountFile.writeAsStringSync(serviceAccountToken); serviceAccountFile.writeAsStringSync(serviceAccountToken);
}); });
test('returns expected commit branch', () {
_processResult = ProcessResult(1, 0, commitBranch, '');
cocoon = Cocoon(
serviceAccountTokenPath: serviceAccountTokenPath,
filesystem: fs,
httpClient: mockClient,
processRunSync: runSyncStub,
);
expect(cocoon.commitBranch, commitBranch);
});
test('returns expected commit sha', () {
_processResult = ProcessResult(1, 0, commitSha, '');
cocoon = Cocoon(
serviceAccountTokenPath: serviceAccountTokenPath,
filesystem: fs,
httpClient: mockClient,
processRunSync: runSyncStub,
);
expect(cocoon.commitSha, commitSha);
});
test('throws exception on git cli errors', () {
_processResult = ProcessResult(1, 1, '', '');
cocoon = Cocoon(
serviceAccountTokenPath: serviceAccountTokenPath,
filesystem: fs,
httpClient: mockClient,
processRunSync: runSyncStub,
);
expect(() => cocoon.commitBranch, throwsA(isA<CocoonException>()));
expect(() => cocoon.commitSha, throwsA(isA<CocoonException>()));
});
test('sends expected request from successful task', () async { test('sends expected request from successful task', () async {
mockClient = MockClient((Request request) async => Response('{}', 200)); mockClient = MockClient((Request request) async => Response('{}', 200));
...@@ -57,26 +111,23 @@ void main() { ...@@ -57,26 +111,23 @@ void main() {
}); });
group('AuthenticatedCocoonClient', () { group('AuthenticatedCocoonClient', () {
const String serviceAccountPath = 'test_account_file';
const String serviceAccountToken = 'test_token';
FileSystem fs; FileSystem fs;
setUp(() { setUp(() {
fs = MemoryFileSystem(); fs = MemoryFileSystem();
final File serviceAccountFile = fs.file(serviceAccountPath)..createSync(); final File serviceAccountFile = fs.file(serviceAccountTokenPath)..createSync();
serviceAccountFile.writeAsStringSync(serviceAccountToken); serviceAccountFile.writeAsStringSync(serviceAccountToken);
}); });
test('reads token from service account file', () { test('reads token from service account file', () {
final AuthenticatedCocoonClient client = AuthenticatedCocoonClient(serviceAccountPath, filesystem: fs); final AuthenticatedCocoonClient client = AuthenticatedCocoonClient(serviceAccountTokenPath, filesystem: fs);
expect(client.serviceAccountToken, serviceAccountToken); expect(client.serviceAccountToken, serviceAccountToken);
}); });
test('reads token from service account file with whitespace', () { test('reads token from service account file with whitespace', () {
final File serviceAccountFile = fs.file(serviceAccountPath)..createSync(); final File serviceAccountFile = fs.file(serviceAccountTokenPath)..createSync();
serviceAccountFile.writeAsStringSync(serviceAccountToken + ' \n'); serviceAccountFile.writeAsStringSync(serviceAccountToken + ' \n');
final AuthenticatedCocoonClient client = AuthenticatedCocoonClient(serviceAccountPath, filesystem: fs); final AuthenticatedCocoonClient client = AuthenticatedCocoonClient(serviceAccountTokenPath, filesystem: fs);
expect(client.serviceAccountToken, serviceAccountToken); expect(client.serviceAccountToken, serviceAccountToken);
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment