blob: 9821ca5a4f16dfc7228b7ea55892b041832d1769 [file] [log] [blame]
// Copyright 2019 The Flutter 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 'package:cocoon_service/src/model/appengine/commit.dart';
import 'package:cocoon_service/src/model/appengine/task.dart';
import 'package:cocoon_service/src/model/luci/push_message.dart';
import 'package:cocoon_service/src/service/luci.dart';
import 'package:gcloud/db.dart';
import 'package:test/test.dart';
import '../src/utilities/entity_generators.dart';
void main() {
group('Task', () {
test('byAttempts comparator', () {
final List<Task> tasks = <Task>[
generateTask(1, attempts: 5),
generateTask(2, attempts: 9),
generateTask(3, attempts: 3),
];
tasks.sort(Task.byAttempts);
expect(tasks.map<int>((Task task) => task.attempts!), <int>[3, 5, 9]);
});
test('disallows illegal status', () {
expect(() => generateTask(1, status: 'unknown'), throwsArgumentError);
expect(() => generateTask(1)..status = 'unknown', throwsArgumentError);
});
test('creates a valid chromebot task', () {
final Key<String> commitKey = generateKey<String>(Commit, 'flutter/flutter/master/42');
const LuciBuilder builder = LuciBuilder(
name: 'builderAbc',
repo: 'flutter/flutter',
taskName: 'taskName',
flaky: false,
);
final Task task = Task.chromebot(commitKey: commitKey, createTimestamp: 123, builder: builder);
validateModel(task);
expect(task.name, 'taskName');
expect(task.builderName, 'builderAbc');
expect(task.createTimestamp, 123);
expect(task.isFlaky, false);
expect(task.requiredCapabilities, <String>['can-update-github']);
expect(task.timeoutInMinutes, 0);
});
test('flaky defaults to false', () {
final Key<String> commitKey = generateKey<String>(Commit, 'flutter/flutter/master/42');
const LuciBuilder builder = LuciBuilder(
name: 'builderAbc',
repo: 'flutter/flutter',
taskName: 'taskName',
flaky: null,
);
expect(Task.chromebot(commitKey: commitKey, createTimestamp: 123, builder: builder).isFlaky, isFalse);
});
group('updateFromBuild', () {
test('updates if buildNumber is null', () {
final DateTime created = DateTime.utc(2022, 1, 11, 1, 1);
final DateTime started = DateTime.utc(2022, 1, 11, 1, 2);
final DateTime completed = DateTime.utc(2022, 1, 11, 1, 3);
final Build build = generatePushMessageBuild(
1,
createdTimestamp: created,
startedTimestamp: started,
completedTimestamp: completed,
);
final Task task = generateTask(1);
expect(task.status, Task.statusNew);
expect(task.buildNumberList, isNull);
expect(task.buildNumber, isNull);
expect(task.endTimestamp, 0);
expect(task.createTimestamp, 0);
expect(task.startTimestamp, 0);
task.updateFromBuild(build);
expect(task.status, Task.statusSucceeded);
expect(task.buildNumber, 1);
expect(task.buildNumberList, '1');
expect(task.createTimestamp, created.millisecondsSinceEpoch);
expect(task.startTimestamp, started.millisecondsSinceEpoch);
expect(task.endTimestamp, completed.millisecondsSinceEpoch);
});
test('defaults timestamps to 0', () {
final Build build = generatePushMessageBuild(1);
final Task task = generateTask(1);
expect(task.endTimestamp, 0);
expect(task.createTimestamp, 0);
expect(task.startTimestamp, 0);
task.updateFromBuild(build);
expect(task.endTimestamp, 0);
expect(task.createTimestamp, 0);
expect(task.startTimestamp, 0);
});
test('updates if buildNumber is prior to pushMessage', () {
final Build build = generatePushMessageBuild(
1,
buildNumber: 2,
status: Status.started,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusSucceeded,
);
expect(task.buildNumberList, '1');
expect(task.status, Task.statusSucceeded);
task.updateFromBuild(build);
expect(task.buildNumber, 2);
expect(task.buildNumberList, '1,2');
expect(task.status, Task.statusInProgress);
});
test('does not duplicate build numbers on multiple messages', () {
final Build build = generatePushMessageBuild(
1,
status: Status.started,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusSucceeded,
);
expect(task.buildNumber, 1);
expect(task.buildNumberList, '1');
expect(task.status, Task.statusSucceeded);
task.updateFromBuild(build);
expect(task.buildNumber, 1);
expect(task.buildNumberList, '1');
expect(task.status, Task.statusSucceeded);
});
test('does not update status if older status', () {
final Build build = generatePushMessageBuild(
1,
status: Status.started,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusSucceeded,
);
expect(task.buildNumber, 1);
expect(task.status, Task.statusSucceeded);
task.updateFromBuild(build);
expect(task.buildNumber, 1);
expect(task.status, Task.statusSucceeded);
});
test('does not update if build is older than task', () {
final Build build = generatePushMessageBuild(
1,
status: Status.completed,
result: Result.success,
);
final Task task = generateTask(
1,
buildNumber: 2,
status: Task.statusNew,
);
expect(task.buildNumber, 2);
expect(task.status, Task.statusNew);
task.updateFromBuild(build);
expect(task.buildNumber, 2);
expect(task.status, Task.statusNew);
});
test('handles cancelled build', () {
final Build build = generatePushMessageBuild(
1,
status: Status.completed,
result: Result.canceled,
);
final Task task = generateTask(
1,
buildNumber: 1,
status: Task.statusNew,
);
expect(task.status, Task.statusNew);
task.updateFromBuild(build);
expect(task.status, Task.statusCancelled);
});
});
});
}
void validateModel(Task task) {
// Throws an exception when property validation fails.
ModelDBImpl().toDatastoreEntity(task);
}