blob: fedd279f22e5c30ed998d34296fa70e1e5ac718d [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_common/task_status.dart';
import 'package:cocoon_server_test/test_logging.dart';
import 'package:cocoon_service/src/service/build_status_service.dart';
import 'package:github/github.dart';
import 'package:test/test.dart';
import '../src/service/fake_firestore_service.dart';
import '../src/utilities/entity_generators.dart';
void main() {
useTestLoggerPerTest();
final newerCommit = generateFirestoreCommit(
1,
sha: 'abc123',
createTimestamp: DateTime(2025, 1, 1, 1, 1).millisecondsSinceEpoch,
);
final olderCommit = generateFirestoreCommit(
2,
sha: 'def456',
createTimestamp: DateTime(2025, 1, 1, 1, 0).millisecondsSinceEpoch,
);
late BuildStatusService buildStatusService;
late FakeFirestoreService firestore;
final slug = RepositorySlug('flutter', 'flutter');
setUp(() {
firestore = FakeFirestoreService();
buildStatusService = BuildStatusService(firestore: firestore);
});
group('calculateStatus', () {
test('returns failure if there are no commits', () async {
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.failure(const []));
});
test('returns success if top commit is all green', () async {
firestore.putDocuments([
newerCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.success());
});
test(
'returns success if top commit is all green followed by red commit',
() async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: olderCommit.sha,
),
]);
firestore.putDocument(
generateFirestoreTask(1, status: TaskStatus.succeeded),
);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.success());
},
);
test('returns failure if last commit contains any red tasks', () async {
firestore.putDocuments([
newerCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: newerCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.failure(const ['task2']));
});
test(
'returns failure if last commit contains any canceled tasks',
() async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: olderCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.failure(const ['task2']));
},
);
test(
'ensure failed task do not have duplicates when last consecutive commits contains red tasks',
() async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: newerCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.failure(const ['task2']));
},
);
test('ignores failures on flaky commits', () async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: newerCommit.sha,
bringup: true,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.success());
});
test(
'returns success if partial green, and all unfinished tasks were last green',
() async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.inProgress,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.success());
},
);
test(
'returns failure if partial green, and any unfinished task was last red',
() async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.inProgress,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: olderCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.failure(const ['task2']));
},
);
test('returns passing when green but a task is rerunning', () async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.inProgress,
attempts: 2,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.success());
});
test('returns failure when a task has an infra failure', () async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.infraFailure,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.failure(const ['task2']));
});
test('returns success when all green with a successful rerun', () async {
firestore.putDocuments([
newerCommit,
olderCommit,
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.succeeded,
commitSha: newerCommit.sha,
attempts: 2,
),
generateFirestoreTask(
1,
status: TaskStatus.succeeded,
commitSha: olderCommit.sha,
),
generateFirestoreTask(
2,
status: TaskStatus.failed,
commitSha: olderCommit.sha,
),
]);
final status = await buildStatusService.calculateCumulativeStatus(slug);
expect(status, BuildStatus.success());
});
test('supports a non-default branch', () async {
firestore.putDocuments([
generateFirestoreCommit(1, branch: 'flutter-0.42-candidate.0'),
generateFirestoreTask(1, commitSha: '1', status: TaskStatus.succeeded),
]);
final status = await buildStatusService.calculateCumulativeStatus(
slug,
branch: 'flutter-0.42-candidate.0',
);
expect(status, BuildStatus.success());
});
});
}