Skip to content

Commit 2989721

Browse files
committed
autocomplete test: Include all the cases which cause results to be recomputed
1 parent 1cab1ea commit 2989721

File tree

2 files changed

+113
-27
lines changed

2 files changed

+113
-27
lines changed

lib/model/store.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,16 @@ class PerAccountStore extends ChangeNotifier with StreamStore {
448448
user.profileData = null;
449449
}
450450
}
451+
// The following two lines will eventually not change [users], but are here to
452+
// trigger a modification to the collection as only changing the properties
453+
// of [user] does not signal a modification of [users] where needed.
454+
//
455+
// One example of this is [MentionAutocompleteView._computeResults] which
456+
// throws a `ConcurrentModificationError` when there is a modification done
457+
// to [users].
458+
users[-1] = user;
459+
users.remove(-1);
460+
451461
autocompleteViewManager.handleRealmUserUpdateEvent(event);
452462
notifyListeners();
453463
} else if (event is StreamEvent) {

test/model/autocomplete_test.dart

Lines changed: 103 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:test/scaffolding.dart';
88
import 'package:zulip/api/model/model.dart';
99
import 'package:zulip/model/autocomplete.dart';
1010
import 'package:zulip/model/narrow.dart';
11+
import 'package:zulip/model/store.dart';
1112
import 'package:zulip/widgets/compose_box.dart';
1213

1314
import '../example_data.dart' as eg;
@@ -274,38 +275,113 @@ void main() {
274275
}
275276
});
276277

277-
test('MentionAutocompleteView mutating store.users while in progress causes '
278-
'the results to be recomputed only one time ', () async {
279-
const narrow = CombinedFeedNarrow();
280-
final store = eg.store();
281-
for (int i = 0; i < 1500; i++) {
282-
await store.addUser(eg.user(userId: i, email: 'user$i@example.com', fullName: 'User $i'));
283-
}
284-
final view = MentionAutocompleteView.init(store: store, narrow: narrow);
278+
group('MentionAutocompleteView recomputes results and does it only one time', () {
279+
Future<void> doCheck({
280+
required List<User> users,
281+
required String rawQuery,
282+
required Future<void> Function(PerAccountStore store) act,
283+
required void Function(Iterable<MentionAutocompleteResult>) expect,
284+
}) async {
285+
const narrow = CombinedFeedNarrow();
286+
final store = eg.store();
287+
await store.addUsers(users);
288+
final view = MentionAutocompleteView.init(store: store, narrow: narrow);
285289

286-
int notifiedCount = 0;
287-
view.addListener(() { notifiedCount++; });
288-
view.query = MentionAutocompleteQuery('User 10000');
290+
int notifiedCount = 0;
291+
view.addListener(() { notifiedCount++; });
292+
view.query = MentionAutocompleteQuery(rawQuery);
289293

290-
await Future(() {});
291-
check(notifiedCount).equals(0);
292-
await store.addUser(eg.user(userId: 10000, email: '[email protected]', fullName: 'User 10000'));
293-
await Future(() {});
294-
check(notifiedCount).equals(0);
295-
await Future(() {});
296-
check(notifiedCount).equals(1);
297-
check(view.results).single
298-
.isA<UserMentionAutocompleteResult>()
299-
.userId.equals(10000);
300-
await Future(() {});
301-
check(notifiedCount).equals(1);
302-
// new result sticks; no "zombie" result from `store.users` pre-mutation
303-
for (int i = 0; i < 10; i++) { // for good measure
304294
await Future(() {});
305-
check(view.results).single
295+
check(notifiedCount).equals(0);
296+
297+
await act(store);
298+
299+
await Future(() {});
300+
check(notifiedCount).equals(0);
301+
await Future(() {});
302+
check(notifiedCount).equals(1);
303+
expect(view.results);
304+
await Future(() {});
305+
check(notifiedCount).equals(1);
306+
// new result sticks; no "zombie" result
307+
for (int i = 0; i < 10; i++) { // for good measure
308+
await Future(() {});
309+
expect(view.results);
310+
}
311+
}
312+
313+
List<User> generateUsers({required int count}) {
314+
return List.generate(
315+
count,
316+
(i) => eg.user(
317+
userId: i,
318+
email: 'user$i@example.com',
319+
fullName: 'User $i'));
320+
}
321+
322+
test('RealmUserAddEvent', () async {
323+
final users = generateUsers(count: 1500);
324+
325+
Future<void> act(PerAccountStore store) async {
326+
await store.addUser(eg.user(
327+
userId: 10000,
328+
329+
fullName: 'User 10000'));
330+
}
331+
332+
void expect(Iterable<MentionAutocompleteResult> results) {
333+
check(results).single
306334
.isA<UserMentionAutocompleteResult>()
307335
.userId.equals(10000);
308-
}
336+
}
337+
338+
await doCheck(
339+
users: users,
340+
rawQuery: 'User 10000',
341+
act: act,
342+
expect: expect,
343+
);
344+
});
345+
346+
test('RealmUserRemoveEvent', () async {
347+
final users = generateUsers(count: 1500);
348+
349+
Future<void> act(PerAccountStore store) async {
350+
await store.removeUser(1495);
351+
}
352+
353+
void expect(Iterable<MentionAutocompleteResult> results) {
354+
check(results).isEmpty();
355+
}
356+
357+
await doCheck(
358+
users: users,
359+
rawQuery: 'User 1495',
360+
act: act,
361+
expect: expect,
362+
);
363+
});
364+
365+
test('RealmUserUpdateEvent', () async {
366+
final users = generateUsers(count: 1500);
367+
368+
Future<void> act(PerAccountStore store) async {
369+
await store.updateUser(1, fullName: 'Updated User 1');
370+
}
371+
372+
void expect(Iterable<MentionAutocompleteResult> results) {
373+
check(results).single
374+
.isA<UserMentionAutocompleteResult>()
375+
.userId.equals(1);
376+
}
377+
378+
await doCheck(
379+
users: users,
380+
rawQuery: 'Updated User 1',
381+
act: act,
382+
expect: expect,
383+
);
384+
});
309385
});
310386

311387
group('MentionAutocompleteQuery.testUser', () {

0 commit comments

Comments
 (0)