Skip to content

Commit ed00d4f

Browse files
committed
Squashed commit of the following:
commit 50d78882baa49482b08ec8083b4c7459624d3de3 Author: Alexander Thomas <[email protected]> Date: Tue Sep 16 12:52:41 2025 +0200 Format commit c602f639127bd369ccb22f3e970ecd097cd9e3d0 Author: Alexander Thomas <[email protected]> Date: Tue Sep 16 12:51:34 2025 +0200 Pre-sort try result to ensure test counts are correct commit b196ece2b33ef6e122bf78b052c02e9eb5b8b9f7 Author: Alexander Thomas <[email protected]> Date: Tue Sep 16 12:40:59 2025 +0200 Improve sorting of result changes commit 47a094e47083cafbca47b9444ad2fad60466ff48 Author: Alexander Thomas <[email protected]> Date: Tue Sep 16 12:40:31 2025 +0200 Formatting commit d4ed80dccfbc5876549141eb6c5f83800c718385 Author: Alexander Thomas <[email protected]> Date: Tue Sep 16 11:19:18 2025 +0200 Remove debug print commit 91d39d6317d0b4c17a9f5fc594592a2f0eddf2f4 Author: Alexander Thomas <[email protected]> Date: Tue Sep 16 11:18:05 2025 +0200 Move no query handling to subclass commit 0e0e2e027f4a32578bb549d5fb440403307d7146 Author: Alexander Thomas <[email protected]> Date: Tue Sep 16 11:09:36 2025 +0200 Fix ChangeInResult for new tests and refactor commit 4bbe46f43b750076250162030591dff8c2bb0c8d Author: Alexander Thomas <[email protected]> Date: Mon Sep 15 13:40:21 2025 +0200 Delete unused changes_list.dart commit cbc573c97fb1d90e1d4b36143167921b5934a633 Author: Alexander Thomas <[email protected]> Date: Wed Sep 10 06:55:25 2025 -0700 Fix copyright headers commit 89de18f13b255d10d6cb131496e945819637aa97 Author: Alexander Thomas <[email protected]> Date: Wed Sep 10 06:43:12 2025 -0700 Upgrade dependencies commit 7b9fbdeca87af7d2e95dcc49cf13671a6ab12ff5 Merge: f8afae5 d2efaee Author: Alexander Thomas <[email protected]> Date: Fri Sep 5 18:39:44 2025 +0200 Merge remote-tracking branch 'origin/main' into try-results-page commit f8afae52af767bf74ad0aba7dfb25434964c4a4b Author: Alexander Thomas <[email protected]> Date: Fri Sep 5 13:55:42 2025 +0200 New implementation of try results page with current results look and feel commit 6e03623b2f17db7fbe8ef9156eb881c5087611af Author: Alexander Thomas <[email protected]> Date: Fri Sep 5 13:54:51 2025 +0200 Add go_router dependency commit e5c1e4a8a9e6b53646b994f4233678c38d703e74 Author: Alexander Thomas <[email protected]> Date: Fri Sep 5 13:52:31 2025 +0200 Fix file header in GEMINI.md commit 2b439b4814a1ecb6c3aa6a1a7a774945f6d3b41e Author: Alexander Thomas <[email protected]> Date: Thu Sep 4 09:11:14 2025 +0200 Add padding left commit f1c614c351575db944c9e1effc7a6736815438dd Author: Alexander Thomas <[email protected]> Date: Wed Sep 3 18:21:45 2025 +0200 Expand rows by default and fix colors commit d02368b58caaa54250d11c817d195bef73a9dffe Author: Alexander Thomas <[email protected]> Date: Wed Sep 3 17:47:55 2025 +0200 Add toolbar and improve header look & feel commit 41df7da8fb2c8e33edea1c54c12dc72859267e22 Author: Alexander Thomas <[email protected]> Date: Wed Sep 3 17:25:47 2025 +0200 Add link to CL and alternate row colors commit b50e95ae79e10851e9cecc82fe90537341c538e5 Author: Alexander Thomas <[email protected]> Date: Wed Sep 3 12:44:39 2025 +0200 Reformat commit 42de59abc70013ef3548fed55438afd2ed771100 Author: Alexander Thomas <[email protected]> Date: Wed Sep 3 12:44:24 2025 +0200 Use conditional import for URL strategy commit b989fe4342ccad7729ad96099314bc8ffe984dd7 Author: Alexander Thomas <[email protected]> Date: Wed Sep 3 12:14:54 2025 +0200 Update README.md and add GEMINI.md commit 891aa19a96a00a1706900b8036be35e05775ad46 Author: Alexander Thomas <[email protected]> Date: Wed Sep 3 12:14:28 2025 +0200 Add missing dependencies commit 1274183a0937ca195c9292406255ba479712eb60 Merge: dee62f5 186e024 Author: Alexander Thomas <[email protected]> Date: Tue Sep 2 16:15:55 2025 +0200 Merge branch 'main' into add-firebase-login commit dee62f5fe53984498aea552659038e61132cd8ee Author: Alexander Thomas <[email protected]> Date: Tue Aug 19 10:11:38 2025 +0200 Add try results page commit 8db60b5 Author: Alexander Thomas <[email protected]> Date: Fri Aug 15 11:28:38 2025 +0200 Format again commit 013e422 Author: Alexander Thomas <[email protected]> Date: Fri Aug 15 11:26:36 2025 +0200 Add copyright headers commit e682745 Author: Alexander Thomas <[email protected]> Date: Fri Aug 15 10:57:46 2025 +0200 Remove extra comments commit ea12229 Author: Alexander Thomas <[email protected]> Date: Fri Aug 15 10:55:02 2025 +0200 dart format commit 9b5b7db Author: Alexander Thomas <[email protected]> Date: Fri Aug 15 10:50:01 2025 +0200 Fix UI and tests commit b2ed2a8 Author: Alexander Thomas <[email protected]> Date: Fri May 9 11:34:52 2025 +0200 WiP to use firebase emulator commit cf6903b Author: Alexander Thomas <[email protected]> Date: Fri May 2 13:08:02 2025 +0200 Remove some redundant comments commit 78c9bb9 Author: Alexander Thomas <[email protected]> Date: Fri May 2 12:22:14 2025 +0200 Make it work commit 97d0a35 Author: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu May 1 15:58:09 2025 +0000 fix: Update Firebase config with values from console Updates the Firebase configuration in current_results_ui/web/index.html with the precise values obtained from the Firebase console, including the correct apiKey and appId.
1 parent 70ef8c0 commit ed00d4f

15 files changed

+891
-74
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:cloud_firestore/cloud_firestore.dart';
6+
7+
class Comment {
8+
final String id;
9+
final String author;
10+
final DateTime created;
11+
final String comment;
12+
final bool? approved;
13+
final List<String> tryResults;
14+
15+
Comment({
16+
required this.id,
17+
required this.author,
18+
required this.created,
19+
required this.comment,
20+
this.approved,
21+
required this.tryResults,
22+
});
23+
24+
factory Comment.fromFirestore(DocumentSnapshot doc) {
25+
final data = doc.data() as Map<String, dynamic>;
26+
return Comment(
27+
id: doc.id,
28+
author: data['author'] ?? '',
29+
created: (data['created'] as Timestamp).toDate(),
30+
comment: data['comment'] ?? '',
31+
approved: data['approved'],
32+
tryResults: List<String>.from(data['try_results'] ?? []),
33+
);
34+
}
35+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:cloud_firestore/cloud_firestore.dart';
6+
7+
class Review {
8+
final String id;
9+
final String subject;
10+
final List<Patchset> patchsets;
11+
12+
Review({required this.id, required this.subject, required this.patchsets});
13+
14+
factory Review.fromFirestore(DocumentSnapshot doc, List<Patchset> patchsets) {
15+
final data = doc.data() as Map<String, dynamic>;
16+
return Review(
17+
id: doc.id,
18+
subject: data['subject'] ?? '',
19+
patchsets: patchsets,
20+
);
21+
}
22+
}
23+
24+
class Patchset {
25+
final String id;
26+
final String description;
27+
final int number;
28+
final int patchsetGroup;
29+
30+
Patchset({
31+
required this.id,
32+
required this.description,
33+
required this.number,
34+
required this.patchsetGroup,
35+
});
36+
37+
factory Patchset.fromFirestore(DocumentSnapshot doc) {
38+
final data = doc.data() as Map<String, dynamic>;
39+
return Patchset(
40+
id: doc.id,
41+
description: data['description'] ?? '',
42+
number: data['number'] ?? 0,
43+
patchsetGroup: data['patchset_group'] ?? 0,
44+
);
45+
}
46+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:cloud_firestore/cloud_firestore.dart';
6+
7+
class TryBuild {
8+
final String id;
9+
final int buildNumber;
10+
final String builder;
11+
final bool success;
12+
final bool completed;
13+
final int review;
14+
final int patchset;
15+
16+
TryBuild({
17+
required this.id,
18+
required this.buildNumber,
19+
required this.builder,
20+
required this.success,
21+
required this.completed,
22+
required this.review,
23+
required this.patchset,
24+
});
25+
26+
factory TryBuild.fromFirestore(DocumentSnapshot doc) {
27+
final data = doc.data() as Map<String, dynamic>;
28+
return TryBuild(
29+
id: doc.id,
30+
buildNumber: data['build_number'] ?? 0,
31+
builder: data['builder'] ?? '',
32+
success: data['success'] ?? false,
33+
completed: data['completed'] ?? false,
34+
review: data['review'] ?? 0,
35+
patchset: data['patchset'] ?? 0,
36+
);
37+
}
38+
}

current_results_ui/lib/query.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'dart:async';
66
import 'dart:collection';
77

88
import 'package:flutter/foundation.dart';
9+
910
import 'package:flutter_current_results/results.dart';
1011
import 'package:http/http.dart' as http;
1112
import 'dart:convert';
@@ -156,25 +157,40 @@ class ChangeInResult implements Comparable<ChangeInResult> {
156157
: ResultKind.fail;
157158

158159
factory ChangeInResult(Result result) {
159-
return ChangeInResult._create(
160+
return ChangeInResult.create(
160161
result: result.result,
161162
expected: result.expected,
162163
isFlaky: result.flaky,
163164
);
164165
}
165166

166-
factory ChangeInResult._create({
167+
factory ChangeInResult.create({
167168
required String result,
168169
required String expected,
169170
required bool isFlaky,
171+
String? previousResult,
170172
}) {
171173
final bool matches = result == expected;
172174
final String text;
173175

174176
if (isFlaky) {
175177
text = 'flaky (latest result $result expected $expected)';
176178
} else {
177-
text = matches ? result : '$result (expected $expected)';
179+
final String resultText = matches
180+
? result
181+
: '$result (expected $expected)';
182+
183+
if (previousResult != null) {
184+
if (previousResult.isNotEmpty) {
185+
text = previousResult == result
186+
? resultText
187+
: '$previousResult -> $resultText';
188+
} else {
189+
text = 'new test => $resultText';
190+
}
191+
} else {
192+
text = resultText;
193+
}
178194
}
179195

180196
return _cache.putIfAbsent(
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:flutter_current_results/src/generated/query.pb.dart';
8+
import 'package:flutter_current_results/src/services/results_service.dart';
9+
10+
import '../../filter.dart';
11+
import '../../query.dart';
12+
13+
class TryQueryResults extends QueryResultsBase {
14+
final int cl;
15+
final int patchset;
16+
final ResultsService _resultsService;
17+
18+
TryQueryResults({
19+
required this.cl,
20+
required this.patchset,
21+
required Filter filter,
22+
ResultsService? resultsService,
23+
}) : _resultsService = resultsService ?? ResultsService(),
24+
super(filter, fetchInitialResults: true, supportsEmptyQuery: true);
25+
26+
@override
27+
Stream<Iterable<(ChangeInResult, Result)>> createResultsStream() async* {
28+
yield await _resultsService.fetchChanges(cl, patchset);
29+
}
30+
}

current_results_ui/lib/src/routing.dart

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:flutter/material.dart';
56
import 'package:go_router/go_router.dart';
7+
import 'package:provider/provider.dart';
68

79
import '../filter.dart';
810
import '../main.dart';
11+
import '../query.dart';
12+
import '../try_results_screen.dart';
13+
import 'data/try_query_results.dart';
914

1015
GoRouter createRouter() => GoRouter(
1116
routes: [
@@ -21,5 +26,35 @@ GoRouter createRouter() => GoRouter(
2126
return CurrentResultsScreen(filter: filter, initialTabIndex: tab);
2227
},
2328
),
29+
GoRoute(
30+
path: '/cl/:cl/:patchset',
31+
builder: (context, state) {
32+
final cl = int.tryParse(state.pathParameters['cl']!);
33+
final patchset = int.tryParse(state.pathParameters['patchset']!);
34+
final filter = Filter(state.uri.queryParameters['filter'] ?? '');
35+
final tab = state.uri.queryParameters.containsKey('showAll')
36+
? 2
37+
: state.uri.queryParameters.containsKey('flaky')
38+
? 1
39+
: 0;
40+
41+
if (cl != null && patchset != null) {
42+
return MultiProvider(
43+
providers: [
44+
ChangeNotifierProvider<TryQueryResults>(
45+
create: (_) =>
46+
TryQueryResults(cl: cl, patchset: patchset, filter: filter),
47+
),
48+
ListenableProxyProvider<TryQueryResults, QueryResultsBase>(
49+
update: (_, tryResults, _) => tryResults,
50+
),
51+
],
52+
child: TryResultsScreen(initialTabIndex: tab),
53+
);
54+
}
55+
// TODO: Should create a proper error screen here.
56+
return const Text('Invalid CL or patchset');
57+
},
58+
),
2459
],
2560
);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:cloud_firestore/cloud_firestore.dart';
6+
7+
class FirestoreService {
8+
static final FirestoreService _instance = FirestoreService._internal();
9+
factory FirestoreService() => _instance;
10+
FirestoreService._internal();
11+
12+
FirebaseFirestore get firestore => FirebaseFirestore.instance;
13+
14+
Future<DocumentSnapshot> fetchReviewInfo(int review) async {
15+
return firestore.doc('reviews/$review').get();
16+
}
17+
18+
Future<List<DocumentSnapshot>> fetchPatchsetInfo(int review) async {
19+
final snapshot = await firestore
20+
.collection('reviews/$review/patchsets')
21+
.orderBy('number')
22+
.get();
23+
return snapshot.docs;
24+
}
25+
26+
Future<List<DocumentSnapshot>> fetchTryChanges(
27+
int review,
28+
int patchset,
29+
) async {
30+
final snapshot = await firestore
31+
.collection('try_results')
32+
.where('review', isEqualTo: review)
33+
.where('patchset', isEqualTo: patchset)
34+
.orderBy('name')
35+
.get();
36+
return snapshot.docs;
37+
}
38+
39+
Future<List<DocumentSnapshot>> fetchTryBuilds(int review) async {
40+
final snapshot = await firestore
41+
.collection('try_builds')
42+
.where('review', isEqualTo: review)
43+
.get();
44+
return snapshot.docs;
45+
}
46+
47+
Future<List<DocumentSnapshot>> fetchCommentsForReview(int review) async {
48+
final snapshot = await firestore
49+
.collection('comments')
50+
.where('review', isEqualTo: review)
51+
.get();
52+
return snapshot.docs;
53+
}
54+
55+
Future<List<DocumentSnapshot>> fetchBuilders() async {
56+
final snapshot = await firestore.collection('configurations').get();
57+
return snapshot.docs;
58+
}
59+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:flutter_current_results/model/comment.dart';
6+
import 'package:flutter_current_results/model/review.dart';
7+
import 'package:flutter_current_results/model/try_build.dart';
8+
import 'package:flutter_current_results/query.dart';
9+
import 'package:flutter_current_results/src/generated/query.pb.dart';
10+
import 'package:flutter_current_results/src/services/firestore_service.dart';
11+
12+
class ResultsService {
13+
final FirestoreService _firestoreService = FirestoreService();
14+
15+
Future<Review> fetchReviewInfo(int review) async {
16+
final patchsetDocs = await _firestoreService.fetchPatchsetInfo(review);
17+
final patchsets = patchsetDocs
18+
.map((d) => Patchset.fromFirestore(d))
19+
.toList();
20+
final reviewDoc = await _firestoreService.fetchReviewInfo(review);
21+
if (reviewDoc.exists) {
22+
return Review.fromFirestore(reviewDoc, patchsets);
23+
}
24+
return Review(
25+
id: review.toString(),
26+
subject: 'No results received yet for CL $review',
27+
patchsets: [],
28+
);
29+
}
30+
31+
Future<Map<String, TryBuild>> fetchBuilds(int review, int patchset) async {
32+
final buildDocs = await _firestoreService.fetchTryBuilds(review);
33+
final builds = buildDocs.map((d) => TryBuild.fromFirestore(d));
34+
return {for (var build in builds) build.builder: build};
35+
}
36+
37+
Future<Iterable<(ChangeInResult, Result)>> fetchChanges(
38+
int review,
39+
int patchset,
40+
) async {
41+
final changeDocs = await _firestoreService.fetchTryChanges(
42+
review,
43+
patchset,
44+
);
45+
return changeDocs.expand((doc) {
46+
final data = doc.data() as Map<String, dynamic>;
47+
final result = data['result'] ?? '';
48+
final change = ChangeInResult.create(
49+
result: result,
50+
expected: data['expected'] ?? '',
51+
isFlaky: result.toLowerCase().contains('flaky'),
52+
previousResult: data['previous_result'] ?? '',
53+
);
54+
final configurations = List<String>.from(data['configurations'] ?? []);
55+
return configurations.map((configuration) {
56+
final result = Result()
57+
..name = data['name'] ?? ''
58+
..configuration = configuration
59+
..result = data['result'] ?? ''
60+
..expected = data['expected'] ?? ''
61+
..flaky = change.flaky;
62+
return (change, result);
63+
});
64+
});
65+
}
66+
67+
Future<List<Comment>> fetchComments(int review) async {
68+
final commentDocs = await _firestoreService.fetchCommentsForReview(review);
69+
return commentDocs.map((d) => Comment.fromFirestore(d)).toList();
70+
}
71+
72+
Future<Map<String, String>> fetchBuilders() async {
73+
final builderDocs = await _firestoreService.fetchBuilders();
74+
return {for (var doc in builderDocs) doc.id: doc.get('builder') as String};
75+
}
76+
}

0 commit comments

Comments
 (0)