blob: cb77181671ee4b701ca7bd0e42bff9a3cdeb7962 [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/cocoon_service.dart';
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/buildbucket.dart';
import 'package:test/test.dart';
import '../../src/datastore/fake_config.dart';
import '../../src/datastore/fake_datastore.dart';
import '../../src/request_handling/fake_pubsub.dart';
import '../../src/request_handling/request_handler_tester.dart';
import '../../src/service/fake_luci_build_service.dart';
import '../../src/service/fake_scheduler.dart';
import '../../src/utilities/entity_generators.dart';
final List<Commit> commits = <Commit>[
generateCommit(3),
generateCommit(2),
generateCommit(1),
];
void main() {
late BatchBackfiller handler;
late RequestHandlerTester tester;
late FakeDatastoreDB db;
late FakePubSub pubsub;
late FakeScheduler scheduler;
group('BatchBackfiller', () {
setUp(() async {
db = FakeDatastoreDB()..addOnQuery<Commit>((Iterable<Commit> results) => commits);
final Config config = FakeConfig(dbValue: db);
pubsub = FakePubSub();
scheduler = FakeScheduler(
config: config,
ciYaml: batchPolicyConfig,
luciBuildService: FakeLuciBuildService(
config: config,
pubsub: pubsub,
),
);
handler = BatchBackfiller(
config: config,
scheduler: scheduler,
);
tester = RequestHandlerTester();
});
test('does not backfill on completed task column', () async {
List<Task> allGreen = <Task>[
generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android A', status: Task.statusSucceeded),
];
db.addOnQuery<Task>((Iterable<Task> results) => allGreen);
await tester.get(handler);
expect(pubsub.messages, isEmpty);
});
test('does not backfill when there is a running task', () async {
List<Task> middleTaskInProgress = <Task>[
generateTask(1, name: 'Linux_android A', status: Task.statusNew),
generateTask(2, name: 'Linux_android A', status: Task.statusInProgress),
generateTask(3, name: 'Linux_android A', status: Task.statusNew),
];
db.addOnQuery<Task>((Iterable<Task> results) => middleTaskInProgress);
await tester.get(handler);
expect(pubsub.messages, isEmpty);
});
test('backfills latest task', () async {
List<Task> allGray = <Task>[
generateTask(1, name: 'Linux_android A', status: Task.statusNew),
generateTask(2, name: 'Linux_android A', status: Task.statusNew),
generateTask(3, name: 'Linux_android A', status: Task.statusNew),
];
db.addOnQuery<Task>((Iterable<Task> results) => allGray);
await tester.get(handler);
expect(pubsub.messages.length, 1);
final ScheduleBuildRequest scheduleBuildRequest =
(pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!;
expect(scheduleBuildRequest.priority, LuciBuildService.kBackfillPriority);
});
test('backfills earlier failed task with higher priority', () async {
List<Task> allGray = <Task>[
generateTask(1, name: 'Linux_android A', status: Task.statusNew),
generateTask(2, name: 'Linux_android A', status: Task.statusNew),
generateTask(3, name: 'Linux_android A', status: Task.statusFailed),
];
db.addOnQuery<Task>((Iterable<Task> results) => allGray);
await tester.get(handler);
expect(pubsub.messages.length, 1);
final ScheduleBuildRequest scheduleBuildRequest =
(pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!;
expect(scheduleBuildRequest.priority, LuciBuildService.kRerunPriority);
});
test('backfills older task', () async {
List<Task> oldestGray = <Task>[
generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android A', status: Task.statusNew),
];
db.addOnQuery<Task>((Iterable<Task> results) => oldestGray);
await tester.get(handler);
expect(pubsub.messages.length, 1);
});
test('updates task as in-progress after backfilling', () async {
List<Task> oldestGray = <Task>[
generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android A', status: Task.statusNew),
];
db.addOnQuery<Task>((Iterable<Task> results) => oldestGray);
final Task task = oldestGray[2];
expect(db.values.length, 0);
expect(task.status, Task.statusNew);
await tester.get(handler);
expect(db.values.length, 1);
expect(task.status, Task.statusInProgress);
});
test('skip scheduling builds if datastore commit fails', () async {
db.commitException = true;
List<Task> oldestGray = <Task>[
generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android A', status: Task.statusNew),
];
db.addOnQuery<Task>((Iterable<Task> results) => oldestGray);
expect(db.values.length, 0);
await tester.get(handler);
expect(db.values.length, 0);
expect(pubsub.messages.length, 0);
});
test('backfills only column A when B does need backfill', () async {
List<Task> scheduleA = <Task>[
// Linux_android A
generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android A', status: Task.statusNew),
// Linux_android B
generateTask(1, name: 'Linux_android B', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android B', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android B', status: Task.statusSucceeded),
];
db.addOnQuery<Task>((Iterable<Task> results) => scheduleA);
await tester.get(handler);
expect(pubsub.messages.length, 1);
});
test('backfills both column A and B', () async {
List<Task> scheduleA = <Task>[
// Linux_android A
generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android A', status: Task.statusNew),
// Linux_android B
generateTask(1, name: 'Linux_android B', status: Task.statusSucceeded),
generateTask(2, name: 'Linux_android B', status: Task.statusSucceeded),
generateTask(3, name: 'Linux_android B', status: Task.statusNew),
];
db.addOnQuery<Task>((Iterable<Task> results) => scheduleA);
await tester.get(handler);
expect(pubsub.messages.length, 2);
});
});
}