Merge pull request #1597 from famedly/nico/stricter-lints

stricter lints
This commit is contained in:
Nicolas Werner 2023-11-17 14:44:00 +01:00 committed by GitHub
commit ee14fb7116
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 620 additions and 584 deletions

View File

@ -2,19 +2,44 @@ include: package:lints/recommended.yaml
linter: linter:
rules: rules:
camel_case_types: true # We enable a few additional rules not in the default and recommended sets.
avoid_print: true # Those in general have low impact on correct code apart from possibly requiring an additional keyword (like async,
constant_identifier_names: true # await, final) or asking you to move a statement to a different line. However many of these linter rules make the
prefer_final_locals: true # code either more uniform or avoid bug prone behaviour. For example not awaiting a future usually is a mistake.
# You can always disable a linter warning with a comment like `// ignore: unawaited_futures`. Please add reasoning,
# why you disable the rule in such cases. This helps others understand, that you explicitly want a behaviour, that
# usually would be considered a mistake.
# Performance (or potential bugs)
# Fixing these warnings makes code easier to optimize for the compiler or prevents leaks.
cancel_subscriptions: true
prefer_final_in_for_each: true prefer_final_in_for_each: true
sort_pub_dependencies: true prefer_final_locals: true
always_use_package_imports: true
# Warn about possible bugs
# Usually code with these warnings indicates a bug.
# Please document if your code explicitly wants such a behaviour.
always_declare_return_types: true always_declare_return_types: true
discarded_futures: true
no_adjacent_strings_in_list: true
test_types_in_equals: true
throw_in_finally: true
unawaited_futures: true
unnecessary_statements: true
unsafe_html: true
# Readability & Style
# These are opinionated choices, where Dart gives us 2 ways to express the same thing.
# This avoids mental overhead by not having to make a choice and making code more uniform to read.
always_use_package_imports: true
avoid_bool_literals_in_conditional_expressions: true
prefer_single_quotes: true prefer_single_quotes: true
sort_child_properties_last: true sort_child_properties_last: true
unawaited_futures: true sort_pub_dependencies: true
unsafe_html: true
avoid_function_literals_in_foreach_calls: false # Be nice to our users and allow them to configure what gets logged.
avoid_print: true
non_constant_identifier_names: false # seems to wrongly diagnose static const variables non_constant_identifier_names: false # seems to wrongly diagnose static const variables
analyzer: analyzer:
@ -22,6 +47,4 @@ analyzer:
todo: ignore todo: ignore
exclude: exclude:
- example/main.dart - example/main.dart
# needed until crypto packages upgrade
- lib/src/database/database.g.dart
plugins: plugins:

View File

@ -100,11 +100,12 @@ class Encryption {
void handleDeviceOneTimeKeysCount( void handleDeviceOneTimeKeysCount(
Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) { Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) {
runInRoot(() => olmManager.handleDeviceOneTimeKeysCount( runInRoot(() async => olmManager.handleDeviceOneTimeKeysCount(
countJson, unusedFallbackKeyTypes)); countJson, unusedFallbackKeyTypes));
} }
void onSync() { void onSync() {
// ignore: discarded_futures
keyVerificationManager.cleanup(); keyVerificationManager.cleanup();
} }
@ -118,30 +119,25 @@ class Encryption {
.contains(event.type)) { .contains(event.type)) {
// "just" room key request things. We don't need these asap, so we handle // "just" room key request things. We don't need these asap, so we handle
// them in the background // them in the background
// ignore: unawaited_futures
runInRoot(() => keyManager.handleToDeviceEvent(event)); runInRoot(() => keyManager.handleToDeviceEvent(event));
} }
if (event.type == EventTypes.Dummy) { if (event.type == EventTypes.Dummy) {
// the previous device just had to create a new olm session, due to olm session // the previous device just had to create a new olm session, due to olm session
// corruption. We want to try to send it the last message we just sent it, if possible // corruption. We want to try to send it the last message we just sent it, if possible
// ignore: unawaited_futures
runInRoot(() => olmManager.handleToDeviceEvent(event)); runInRoot(() => olmManager.handleToDeviceEvent(event));
} }
if (event.type.startsWith('m.key.verification.')) { if (event.type.startsWith('m.key.verification.')) {
// some key verification event. No need to handle it now, we can easily // some key verification event. No need to handle it now, we can easily
// do this in the background // do this in the background
// ignore: unawaited_futures
runInRoot(() => keyVerificationManager.handleToDeviceEvent(event)); runInRoot(() => keyVerificationManager.handleToDeviceEvent(event));
} }
if (event.type.startsWith('m.secret.')) { if (event.type.startsWith('m.secret.')) {
// some ssss thing. We can do this in the background // some ssss thing. We can do this in the background
// ignore: unawaited_futures
runInRoot(() => ssss.handleToDeviceEvent(event)); runInRoot(() => ssss.handleToDeviceEvent(event));
} }
if (event.sender == client.userID) { if (event.sender == client.userID) {
// maybe we need to re-try SSSS secrets // maybe we need to re-try SSSS secrets
// ignore: unawaited_futures
runInRoot(() => ssss.periodicallyRequestMissingCache()); runInRoot(() => ssss.periodicallyRequestMissingCache());
} }
} }
@ -157,14 +153,11 @@ class Encryption {
update.content['content']['msgtype'] update.content['content']['msgtype']
.startsWith('m.key.verification.'))) { .startsWith('m.key.verification.'))) {
// "just" key verification, no need to do this in sync // "just" key verification, no need to do this in sync
// ignore: unawaited_futures
runInRoot(() => keyVerificationManager.handleEventUpdate(update)); runInRoot(() => keyVerificationManager.handleEventUpdate(update));
} }
if (update.content['sender'] == client.userID && if (update.content['sender'] == client.userID &&
update.content['unsigned']?['transaction_id'] == null) { update.content['unsigned']?['transaction_id'] == null) {
// maybe we need to re-try SSSS secrets // maybe we need to re-try SSSS secrets
// ignore: unawaited_futures
runInRoot(() => ssss.periodicallyRequestMissingCache()); runInRoot(() => ssss.periodicallyRequestMissingCache());
} }
} }
@ -239,6 +232,7 @@ class Encryption {
// the entry should always exist. In the case it doesn't, the following // the entry should always exist. In the case it doesn't, the following
// line *could* throw an error. As that is a future, though, and we call // line *could* throw an error. As that is a future, though, and we call
// it un-awaited here, nothing happens, which is exactly the result we want // it un-awaited here, nothing happens, which is exactly the result we want
// ignore: discarded_futures
client.database?.updateInboundGroupSessionIndexes( client.database?.updateInboundGroupSessionIndexes(
json.encode(inboundGroupSession.indexes), roomId, sessionId); json.encode(inboundGroupSession.indexes), roomId, sessionId);
} }
@ -252,7 +246,7 @@ class Encryption {
?.session_id() ?? ?.session_id() ??
'') == '') ==
content.sessionId) { content.sessionId) {
runInRoot(() => runInRoot(() async =>
keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true)); keyManager.clearOrUseOutboundGroupSession(roomId, wipe: true));
} }
if (canRequestSession) { if (canRequestSession) {

View File

@ -244,7 +244,8 @@ class KeyManager {
!client.isUnknownSession) { !client.isUnknownSession) {
// do e2ee recovery // do e2ee recovery
_requestedSessionIds.add(requestIdent); _requestedSessionIds.add(requestIdent);
runInRoot(() => request(
runInRoot(() async => request(
room, room,
sessionId, sessionId,
senderKey, senderKey,
@ -775,8 +776,8 @@ class KeyManager {
Future<void>? _uploadingFuture; Future<void>? _uploadingFuture;
void startAutoUploadKeys() { void startAutoUploadKeys() {
_uploadKeysOnSync = encryption.client.onSync.stream _uploadKeysOnSync = encryption.client.onSync.stream.listen(
.listen((_) => uploadInboundGroupSessions(skipIfInProgress: true)); (_) async => uploadInboundGroupSessions(skipIfInProgress: true));
} }
/// This task should be performed after sync processing but should not block /// This task should be performed after sync processing but should not block
@ -1064,6 +1065,7 @@ class KeyManager {
StreamSubscription<SyncUpdate>? _uploadKeysOnSync; StreamSubscription<SyncUpdate>? _uploadKeysOnSync;
void dispose() { void dispose() {
// ignore: discarded_futures
_uploadKeysOnSync?.cancel(); _uploadKeysOnSync?.cancel();
for (final sess in _outboundGroupSessions.values) { for (final sess in _outboundGroupSessions.values) {
sess.dispose(); sess.dispose();

View File

@ -396,20 +396,24 @@ class OlmManager {
final device = client.userDeviceKeys[event.sender]?.deviceKeys.values final device = client.userDeviceKeys[event.sender]?.deviceKeys.values
.firstWhereOrNull((d) => d.curve25519Key == senderKey); .firstWhereOrNull((d) => d.curve25519Key == senderKey);
final existingSessions = olmSessions[senderKey]; final existingSessions = olmSessions[senderKey];
Future<void> updateSessionUsage([OlmSession? session]) => Future<void> updateSessionUsage([OlmSession? session]) async {
runInRoot(() async { try {
if (session != null) { if (session != null) {
session.lastReceived = DateTime.now(); session.lastReceived = DateTime.now();
await storeOlmSession(session); await storeOlmSession(session);
} }
if (device != null) { if (device != null) {
device.lastActive = DateTime.now(); device.lastActive = DateTime.now();
await encryption.olmDatabase?.setLastActiveUserDeviceKey( await encryption.olmDatabase?.setLastActiveUserDeviceKey(
device.lastActive.millisecondsSinceEpoch, device.lastActive.millisecondsSinceEpoch,
device.userId, device.userId,
device.deviceId!); device.deviceId!);
} }
}); } catch (e, s) {
Logs().e('Error while updating olm session timestamp', e, s);
}
}
if (existingSessions != null) { if (existingSessions != null) {
for (final session in existingSessions) { for (final session in existingSessions) {
if (session.session == null) { if (session.session == null) {
@ -446,14 +450,16 @@ class OlmManager {
newSession.create_inbound_from(_olmAccount!, senderKey, body); newSession.create_inbound_from(_olmAccount!, senderKey, body);
_olmAccount!.remove_one_time_keys(newSession); _olmAccount!.remove_one_time_keys(newSession);
await encryption.olmDatabase?.updateClientKeys(pickledOlmAccount!); await encryption.olmDatabase?.updateClientKeys(pickledOlmAccount!);
plaintext = newSession.decrypt(type, body); plaintext = newSession.decrypt(type, body);
await runInRoot(() => storeOlmSession(OlmSession(
key: client.userID!, await storeOlmSession(OlmSession(
identityKey: senderKey, key: client.userID!,
sessionId: newSession.session_id(), identityKey: senderKey,
session: newSession, sessionId: newSession.session_id(),
lastReceived: DateTime.now(), session: newSession,
))); lastReceived: DateTime.now(),
));
await updateSessionUsage(); await updateSessionUsage();
} catch (e) { } catch (e) {
newSession.free(); newSession.free();
@ -570,8 +576,6 @@ class OlmManager {
return _decryptToDeviceEvent(event); return _decryptToDeviceEvent(event);
} catch (_) { } catch (_) {
// okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one // okay, the thing errored while decrypting. It is safe to assume that the olm session is corrupt and we should generate a new one
// ignore: unawaited_futures
runInRoot(() => restoreOlmSession(event.senderId, senderKey)); runInRoot(() => restoreOlmSession(event.senderId, senderKey));
rethrow; rethrow;
@ -658,14 +662,18 @@ class OlmManager {
final encryptResult = sess.first.session!.encrypt(json.encode(fullPayload)); final encryptResult = sess.first.session!.encrypt(json.encode(fullPayload));
await storeOlmSession(sess.first); await storeOlmSession(sess.first);
if (encryption.olmDatabase != null) { if (encryption.olmDatabase != null) {
await runInRoot( try {
() async => encryption.olmDatabase?.setLastSentMessageUserDeviceKey( await encryption.olmDatabase?.setLastSentMessageUserDeviceKey(
json.encode({ json.encode({
'type': type, 'type': type,
'content': payload, 'content': payload,
}), }),
device.userId, device.userId,
device.deviceId!)); device.deviceId!);
} catch (e, s) {
// we can ignore this error, since it would just make us use a different olm session possibly
Logs().w('Error while updating olm usage timestamp', e, s);
}
} }
final encryptedBody = <String, dynamic>{ final encryptedBody = <String, dynamic>{
'algorithm': AlgorithmTypes.olmV1Curve25519AesSha2, 'algorithm': AlgorithmTypes.olmV1Curve25519AesSha2,

View File

@ -31,7 +31,6 @@ import 'package:matrix/encryption/utils/ssss_cache.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:matrix/src/utils/cached_stream_controller.dart'; import 'package:matrix/src/utils/cached_stream_controller.dart';
import 'package:matrix/src/utils/crypto/crypto.dart' as uc; import 'package:matrix/src/utils/crypto/crypto.dart' as uc;
import 'package:matrix/src/utils/run_in_root.dart';
const cacheTypes = <String>{ const cacheTypes = <String>{
EventTypes.CrossSigningSelfSigning, EventTypes.CrossSigningSelfSigning,
@ -722,7 +721,11 @@ class OpenSSSS {
throw InvalidPassphraseException('Inalid key'); throw InvalidPassphraseException('Inalid key');
} }
if (postUnlock) { if (postUnlock) {
await runInRoot(() => _postUnlock()); try {
await _postUnlock();
} catch (e, s) {
Logs().e('Error during post unlock', e, s);
}
} }
} }

View File

@ -3,7 +3,7 @@ import 'dart:typed_data';
/// decodes base64 /// decodes base64
/// ///
/// Dart's native [base64.decode] requires a padded base64 input String. /// Dart's native [base64.decode()] requires a padded base64 input String.
/// This function allows unpadded base64 too. /// This function allows unpadded base64 too.
/// ///
/// See: https://github.com/dart-lang/sdk/issues/39510 /// See: https://github.com/dart-lang/sdk/issues/39510

View File

@ -267,11 +267,8 @@ class KeyVerification {
} }
/// `qrCanWork` - qr cannot work if we are verifying another master key but our own is unverified /// `qrCanWork` - qr cannot work if we are verifying another master key but our own is unverified
final qrCanWork = (userId != client.userID) final qrCanWork = (userId == client.userID) ||
? ((client.userDeviceKeys[client.userID]?.masterKey?.verified ?? false) ((client.userDeviceKeys[client.userID]?.masterKey?.verified ?? false));
? true
: false)
: true;
if (client.verificationMethods.contains(KeyVerificationMethod.qrShow) && if (client.verificationMethods.contains(KeyVerificationMethod.qrShow) &&
qrCanWork) { qrCanWork) {

View File

@ -1636,7 +1636,7 @@ class Client extends MatrixApi {
set backgroundSync(bool enabled) { set backgroundSync(bool enabled) {
_backgroundSync = enabled; _backgroundSync = enabled;
if (_backgroundSync) { if (_backgroundSync) {
_sync(); runInRoot(() async => _sync());
} }
} }
@ -2232,7 +2232,7 @@ class Client extends MatrixApi {
requestHistoryOnLimitedTimeline) { requestHistoryOnLimitedTimeline) {
Logs().v( Logs().v(
'Limited timeline for ${rooms[roomIndex].id} request history now'); 'Limited timeline for ${rooms[roomIndex].id} request history now');
unawaited(runInRoot(rooms[roomIndex].requestHistory)); runInRoot(rooms[roomIndex].requestHistory);
} }
} }
return room; return room;

View File

@ -612,7 +612,7 @@ class HiveCollectionsDatabase extends DatabaseApi {
// post-load the heroes // post-load the heroes
final heroes = room.summary.mHeroes; final heroes = room.summary.mHeroes;
if (heroes != null) { if (heroes != null) {
heroes.forEach((hero) => membersToPostload.add(hero)); membersToPostload.addAll(heroes);
} }
} }
// Load members // Load members
@ -771,9 +771,9 @@ class HiveCollectionsDatabase extends DatabaseApi {
.toList(); .toList();
final states = await _roomMembersBox.getAll(keys); final states = await _roomMembersBox.getAll(keys);
states.removeWhere((state) => state == null); states.removeWhere((state) => state == null);
states.forEach( for (final state in states) {
(state) => users.add(Event.fromJson(copyMap(state!), room).asUser), users.add(Event.fromJson(copyMap(state!), room).asUser);
); }
return users; return users;
} }

View File

@ -128,6 +128,7 @@ class Event extends MatrixEvent {
final json = toJson(); final json = toJson();
json['unsigned'] ??= <String, dynamic>{}; json['unsigned'] ??= <String, dynamic>{};
json['unsigned'][messageSendingStatusKey] = EventStatus.error.intValue; json['unsigned'][messageSendingStatusKey] = EventStatus.error.intValue;
// ignore: discarded_futures
room.client.handleSync( room.client.handleSync(
SyncUpdate( SyncUpdate(
nextBatch: '', nextBatch: '',
@ -154,6 +155,7 @@ class Event extends MatrixEvent {
MessageTypes.File, MessageTypes.File,
}.contains(messageType) && }.contains(messageType) &&
!room.sendingFilePlaceholders.containsKey(eventId)) { !room.sendingFilePlaceholders.containsKey(eventId)) {
// ignore: discarded_futures
remove(); remove();
} }
} }

View File

@ -1620,6 +1620,7 @@ class Room {
return user.asUser; return user.asUser;
} else { } else {
if (mxID.isValidMatrixId) { if (mxID.isValidMatrixId) {
// ignore: discarded_futures
requestUser( requestUser(
mxID, mxID,
ignoreErrors: true, ignoreErrors: true,
@ -2280,7 +2281,7 @@ class Room {
} }
final Map<String, int> servers = {}; final Map<String, int> servers = {};
users.forEach((user) { for (final user in users) {
if (user.id.domain != null) { if (user.id.domain != null) {
if (servers.containsKey(user.id.domain!)) { if (servers.containsKey(user.id.domain!)) {
servers[user.id.domain!] = servers[user.id.domain!]! + 1; servers[user.id.domain!] = servers[user.id.domain!]! + 1;
@ -2288,7 +2289,7 @@ class Room {
servers[user.id.domain!] = 1; servers[user.id.domain!] = 1;
} }
} }
}); }
final sortedServers = Map.fromEntries(servers.entries.toList() final sortedServers = Map.fromEntries(servers.entries.toList()
..sort((e1, e2) => e1.value.compareTo(e2.value))); ..sort((e1, e2) => e1.value.compareTo(e2.value)));
for (var i = 0; i <= 2; i++) { for (var i = 0; i <= 2; i++) {

View File

@ -300,8 +300,11 @@ class Timeline {
/// Don't forget to call this before you dismiss this object! /// Don't forget to call this before you dismiss this object!
void cancelSubscriptions() { void cancelSubscriptions() {
// ignore: discarded_futures
sub?.cancel(); sub?.cancel();
// ignore: discarded_futures
roomSub?.cancel(); roomSub?.cancel();
// ignore: discarded_futures
sessionIdReceivedSub?.cancel(); sessionIdReceivedSub?.cancel();
} }

View File

@ -433,17 +433,16 @@ class DeviceKeys extends SignableKey {
bool? _validSelfSignature; bool? _validSelfSignature;
bool get selfSigned => bool get selfSigned =>
_validSelfSignature ?? _validSelfSignature ??
(_validSelfSignature = (deviceId != null && (_validSelfSignature = deviceId != null &&
signatures signatures
?.tryGetMap<String, Object?>(userId) ?.tryGetMap<String, Object?>(userId)
?.tryGet<String>('ed25519:$deviceId') == ?.tryGet<String>('ed25519:$deviceId') !=
null null &&
? false
// without libolm we still want to be able to add devices. In that case we ofc just can't // without libolm we still want to be able to add devices. In that case we ofc just can't
// verify the signature // verify the signature
: _verifySignature( _verifySignature(
ed25519Key!, signatures![userId]!['ed25519:$deviceId']!, ed25519Key!, signatures![userId]!['ed25519:$deviceId']!,
isSignatureWithoutLibolmValid: true))); isSignatureWithoutLibolmValid: true));
@override @override
bool get blocked => super.blocked || !selfSigned; bool get blocked => super.blocked || !selfSigned;
@ -505,7 +504,7 @@ class DeviceKeys extends SignableKey {
lastActive = DateTime.fromMillisecondsSinceEpoch(0); lastActive = DateTime.fromMillisecondsSinceEpoch(0);
} }
KeyVerification startVerification() { Future<KeyVerification> startVerification() async {
if (!isValid) { if (!isValid) {
throw Exception('setVerification called on invalid key'); throw Exception('setVerification called on invalid key');
} }
@ -517,7 +516,7 @@ class DeviceKeys extends SignableKey {
final request = KeyVerification( final request = KeyVerification(
encryption: encryption, userId: userId, deviceId: deviceId!); encryption: encryption, userId: userId, deviceId: deviceId!);
request.start(); await request.start();
encryption.keyVerificationManager.addRequest(request); encryption.keyVerificationManager.addRequest(request);
return request; return request;
} }

View File

@ -50,10 +50,9 @@ abstract class NativeImplementations {
bool retryInDummy = false, bool retryInDummy = false,
}); });
@override
/// this implementation will catch any non-implemented method /// this implementation will catch any non-implemented method
dynamic noSuchMethod(Invocation invocation) { @override
dynamic noSuchMethod(Invocation invocation) async {
final dynamic argument = invocation.positionalArguments.single; final dynamic argument = invocation.positionalArguments.single;
final memberName = invocation.memberName.toString().split('"')[1]; final memberName = invocation.memberName.toString().split('"')[1];

View File

@ -20,13 +20,13 @@ import 'dart:async';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
Future<T?> runInRoot<T>(FutureOr<T> Function() fn) async { void runInRoot<T>(FutureOr<T> Function() fn) {
return await Zone.root.run(() async { // ignore: discarded_futures
Zone.root.run(() async {
try { try {
return await fn(); await fn();
} catch (e, s) { } catch (e, s) {
Logs().e('Error thrown in root zone', e, s); Logs().e('Error thrown in root zone', e, s);
} }
return null;
}); });
} }

View File

@ -52,6 +52,7 @@ class UiaRequest<T> {
} }
UiaRequest({this.onUpdate, required this.request}) { UiaRequest({this.onUpdate, required this.request}) {
// ignore: discarded_futures
_run(); _run();
} }

View File

@ -37,7 +37,7 @@ class NativeImplementationsWebWorker extends NativeImplementations {
return completer.future.timeout(timeout); return completer.future.timeout(timeout);
} }
void _handleIncomingMessage(MessageEvent event) { Future<void> _handleIncomingMessage(MessageEvent event) async {
final data = event.data; final data = event.data;
// don't forget handling errors of our second thread... // don't forget handling errors of our second thread...
if (data['label'] == 'stacktrace') { if (data['label'] == 'stacktrace') {
@ -46,12 +46,10 @@ class NativeImplementationsWebWorker extends NativeImplementations {
final error = event.data['error']!; final error = event.data['error']!;
Future.value( final stackTrace =
onStackTrace.call(event.data['stacktrace'] as String), await onStackTrace.call(event.data['stacktrace'] as String);
).then( completer?.completeError(
(stackTrace) => completer?.completeError( WebWorkerError(error: error, stackTrace: stackTrace),
WebWorkerError(error: error, stackTrace: stackTrace),
),
); );
} else { } else {
final response = WebWorkerData.fromJson(event.data); final response = WebWorkerData.fromJson(event.data);

View File

@ -522,17 +522,18 @@ class CallSession {
await gotCallFeedsForAnswer(callFeeds); await gotCallFeedsForAnswer(callFeeds);
} }
void replacedBy(CallSession newCall) { Future<void> replacedBy(CallSession newCall) async {
if (state == CallState.kWaitLocalMedia) { if (state == CallState.kWaitLocalMedia) {
Logs().v('Telling new call to wait for local media'); Logs().v('Telling new call to wait for local media');
newCall.waitForLocalAVStream = true; newCall.waitForLocalAVStream = true;
} else if (state == CallState.kCreateOffer || } else if (state == CallState.kCreateOffer ||
state == CallState.kInviteSent) { state == CallState.kInviteSent) {
Logs().v('Handing local stream to new call'); Logs().v('Handing local stream to new call');
newCall.gotCallFeedsForAnswer(getLocalStreams); await newCall.gotCallFeedsForAnswer(getLocalStreams);
} }
successor = newCall; successor = newCall;
onCallReplaced.add(newCall); onCallReplaced.add(newCall);
// ignore: unawaited_futures
hangup(CallErrorCode.Replaced, true); hangup(CallErrorCode.Replaced, true);
} }
@ -698,7 +699,7 @@ class CallSession {
Logs().i( Logs().i(
'Stream purpose update: \nid = "$streamId", \npurpose = "${sdpStreamMetadata.purpose}", \naudio_muted = ${sdpStreamMetadata.audio_muted}, \nvideo_muted = ${sdpStreamMetadata.video_muted}'); 'Stream purpose update: \nid = "$streamId", \npurpose = "${sdpStreamMetadata.purpose}", \naudio_muted = ${sdpStreamMetadata.audio_muted}, \nvideo_muted = ${sdpStreamMetadata.video_muted}');
}); });
getRemoteStreams.forEach((wpstream) { for (final wpstream in getRemoteStreams) {
final streamId = wpstream.stream!.id; final streamId = wpstream.stream!.id;
final purpose = metadata.sdpStreamMetadatas[streamId]; final purpose = metadata.sdpStreamMetadatas[streamId];
if (purpose != null) { if (purpose != null) {
@ -712,7 +713,7 @@ class CallSession {
wpstream.stopped = true; wpstream.stopped = true;
fireCallEvent(CallEvent.kFeedsChanged); fireCallEvent(CallEvent.kFeedsChanged);
} }
}); }
} }
Future<void> onSDPStreamMetadataReceived(SDPStreamMetadata metadata) async { Future<void> onSDPStreamMetadataReceived(SDPStreamMetadata metadata) async {
@ -1510,17 +1511,18 @@ class CallSession {
return pc; return pc;
} }
void createDataChannel(String label, RTCDataChannelInit dataChannelDict) { Future<void> createDataChannel(
pc?.createDataChannel(label, dataChannelDict); String label, RTCDataChannelInit dataChannelDict) async {
await pc?.createDataChannel(label, dataChannelDict);
} }
Future<void> tryRemoveStopedStreams() async { Future<void> tryRemoveStopedStreams() async {
final removedStreams = <String, WrappedMediaStream>{}; final removedStreams = <String, WrappedMediaStream>{};
streams.forEach((stream) { for (final stream in streams) {
if (stream.stopped) { if (stream.stopped) {
removedStreams[stream.stream!.id] = stream; removedStreams[stream.stream!.id] = stream;
} }
}); }
streams streams
.removeWhere((stream) => removedStreams.containsKey(stream.stream!.id)); .removeWhere((stream) => removedStreams.containsKey(stream.stream!.id));
for (final element in removedStreams.entries) { for (final element in removedStreams.entries) {
@ -1561,9 +1563,9 @@ class CallSession {
try { try {
if (candidatesQueue.isNotEmpty) { if (candidatesQueue.isNotEmpty) {
final candidates = <Map<String, dynamic>>[]; final candidates = <Map<String, dynamic>>[];
candidatesQueue.forEach((element) { for (final element in candidatesQueue) {
candidates.add(element.toMap()); candidates.add(element.toMap());
}); }
localCandidates = []; localCandidates = [];
final res = await sendCallCandidates( final res = await sendCallCandidates(
opts.room, callId, localPartyId, candidates); opts.room, callId, localPartyId, candidates);

View File

@ -45,11 +45,15 @@ class ConnectionTester {
await pc1!.setRemoteDescription(answer); await pc1!.setRemoteDescription(answer);
void dispose() { Future<void> dispose() async {
pc1!.close(); await Future.wait([
pc1!.dispose(); pc1!.close(),
pc2!.close(); pc2!.close(),
pc2!.dispose(); ]);
await Future.wait([
pc1!.dispose(),
pc2!.dispose(),
]);
} }
bool connected = false; bool connected = false;
@ -69,6 +73,7 @@ class ConnectionTester {
.e('[VOIP] ConnectionTester Error while testing TURN server: ', e, s); .e('[VOIP] ConnectionTester Error while testing TURN server: ', e, s);
} }
// ignore: unawaited_futures
dispose(); dispose();
return connected; return connected;
} }

View File

@ -150,8 +150,9 @@ class IGroupCallRoomMemberState {
List<IGroupCallRoomMemberCallState> calls = []; List<IGroupCallRoomMemberCallState> calls = [];
IGroupCallRoomMemberState.fromJson(MatrixEvent event) { IGroupCallRoomMemberState.fromJson(MatrixEvent event) {
if (event.content['m.calls'] != null) { if (event.content['m.calls'] != null) {
(event.content['m.calls'] as List<dynamic>).forEach( for (final call in (event.content['m.calls'] as List<dynamic>)) {
(call) => calls.add(IGroupCallRoomMemberCallState.fromJson(call))); calls.add(IGroupCallRoomMemberCallState.fromJson(call));
}
} }
} }
} }
@ -231,11 +232,11 @@ class GroupCall {
this.groupCallId = groupCallId ?? genCallID(); this.groupCallId = groupCallId ?? genCallID();
} }
GroupCall create() { Future<GroupCall> create() async {
voip.groupCalls[groupCallId] = this; voip.groupCalls[groupCallId] = this;
voip.groupCalls[room.id] = this; voip.groupCalls[room.id] = this;
client.setRoomStateWithKey( await client.setRoomStateWithKey(
room.id, room.id,
EventTypes.GroupCallPrefix, EventTypes.GroupCallPrefix,
groupCallId, groupCallId,
@ -276,12 +277,12 @@ class GroupCall {
final List<MatrixEvent> events = []; final List<MatrixEvent> events = [];
final roomStates = await client.getRoomState(room.id); final roomStates = await client.getRoomState(room.id);
roomStates.sort((a, b) => a.originServerTs.compareTo(b.originServerTs)); roomStates.sort((a, b) => a.originServerTs.compareTo(b.originServerTs));
roomStates.forEach((value) { for (final value in roomStates) {
if (value.type == EventTypes.GroupCallMemberPrefix && if (value.type == EventTypes.GroupCallMemberPrefix &&
!room.callMemberStateIsExpired(value, groupCallId)) { !room.callMemberStateIsExpired(value, groupCallId)) {
events.add(value); events.add(value);
} }
}); }
return events; return events;
} }
@ -412,6 +413,7 @@ class GroupCall {
if (localUserMediaStream != null) { if (localUserMediaStream != null) {
final oldStream = localUserMediaStream!.stream; final oldStream = localUserMediaStream!.stream;
localUserMediaStream!.setNewStream(stream.stream!); localUserMediaStream!.setNewStream(stream.stream!);
// ignore: discarded_futures
stopMediaStream(oldStream); stopMediaStream(oldStream);
} }
} }

View File

@ -23,9 +23,9 @@ Future<void> stopMediaStream(MediaStream? stream) async {
} }
void setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) { void setTracksEnabled(List<MediaStreamTrack> tracks, bool enabled) {
tracks.forEach((element) { for (final element in tracks) {
element.enabled = enabled; element.enabled = enabled;
}); }
} }
Future<bool> hasAudioDevice() async { Future<bool> hasAudioDevice() async {

View File

@ -53,6 +53,7 @@ class VoIP {
for (final room in client.rooms) { for (final room in client.rooms) {
if (room.activeGroupCallEvents.isNotEmpty) { if (room.activeGroupCallEvents.isNotEmpty) {
for (final groupCall in room.activeGroupCallEvents) { for (final groupCall in room.activeGroupCallEvents) {
// ignore: discarded_futures
createGroupCallFromRoomStateEvent(groupCall, createGroupCallFromRoomStateEvent(groupCall,
emitHandleNewGroupCall: false); emitHandleNewGroupCall: false);
} }
@ -589,7 +590,7 @@ class VoIP {
return null; return null;
} }
final groupId = genCallID(); final groupId = genCallID();
final groupCall = GroupCall( final groupCall = await GroupCall(
groupCallId: groupId, groupCallId: groupId,
client: client, client: client,
voip: this, voip: this,

View File

@ -1137,8 +1137,8 @@ void main() {
reason: '!5345234235:example.com not found as archived room'); reason: '!5345234235:example.com not found as archived room');
}); });
tearDown(() { tearDown(() async {
matrix.dispose(closeDatabase: true); await matrix.dispose(closeDatabase: true);
}); });
}); });
} }

View File

@ -28,9 +28,450 @@ import 'fake_database.dart';
void main() { void main() {
group('HiveCollections Database Test', () { group('HiveCollections Database Test', () {
testDatabase( late DatabaseApi database;
getHiveCollectionsDatabase(null), late int toDeviceQueueIndex;
); test('Open', () async {
database = await getHiveCollectionsDatabase(null);
});
test('transaction', () async {
var counter = 0;
await database.transaction(() async {
expect(counter++, 0);
await database.transaction(() async {
expect(counter++, 1);
await Future.delayed(Duration(milliseconds: 50));
expect(counter++, 2);
});
expect(counter++, 3);
});
});
test('insertIntoToDeviceQueue', () async {
toDeviceQueueIndex = await database.insertIntoToDeviceQueue(
'm.test',
'txnId',
'{"foo":"bar"}',
);
});
test('getToDeviceEventQueue', () async {
final toDeviceQueue = await database.getToDeviceEventQueue();
expect(toDeviceQueue.first.type, 'm.test');
});
test('deleteFromToDeviceQueue', () async {
await database.deleteFromToDeviceQueue(toDeviceQueueIndex);
final toDeviceQueue = await database.getToDeviceEventQueue();
expect(toDeviceQueue.isEmpty, true);
});
test('storeFile', () async {
await database.storeFile(
Uri.parse('mxc://test'), Uint8List.fromList([0]), 0);
final file = await database.getFile(Uri.parse('mxc://test'));
expect(file != null, database.supportsFileStoring);
});
test('getFile', () async {
await database.getFile(Uri.parse('mxc://test'));
});
test('deleteOldFiles', () async {
await database.deleteOldFiles(1);
final file = await database.getFile(Uri.parse('mxc://test'));
expect(file == null, true);
});
test('storeRoomUpdate', () async {
final roomUpdate = JoinedRoomUpdate.fromJson({
'highlight_count': 0,
'notification_count': 0,
'limited_timeline': false,
'membership': Membership.join,
});
final client = Client('testclient');
await database.storeRoomUpdate('!testroom', roomUpdate, client);
final rooms = await database.getRoomList(client);
expect(rooms.single.id, '!testroom');
});
test('getRoomList', () async {
final room =
await database.getSingleRoom(Client('testclient'), '!testroom');
expect(room?.id, '!testroom');
});
test('getRoomList', () async {
final list = await database.getRoomList(Client('testclient'));
expect(list.single.id, '!testroom');
});
test('setRoomPrevBatch', () async {
final client = Client('testclient');
await database.setRoomPrevBatch('1234', '!testroom', client);
final rooms = await database.getRoomList(client);
expect(rooms.single.prev_batch, '1234');
});
test('forgetRoom', () async {
await database.forgetRoom('!testroom');
final rooms = await database.getRoomList(Client('testclient'));
expect(rooms.isEmpty, true);
});
test('getClient', () async {
await database.getClient('name');
});
test('insertClient', () async {
await database.insertClient(
'name',
'homeserverUrl',
'token',
'userId',
'deviceId',
'deviceName',
'prevBatch',
'olmAccount',
);
final client = await database.getClient('name');
expect(client?['token'], 'token');
});
test('updateClient', () async {
await database.updateClient(
'homeserverUrl',
'token_different',
'userId',
'deviceId',
'deviceName',
'prevBatch',
'olmAccount',
);
final client = await database.getClient('name');
expect(client?['token'], 'token_different');
});
test('updateClientKeys', () async {
await database.updateClientKeys(
'olmAccount2',
);
final client = await database.getClient('name');
expect(client?['olm_account'], 'olmAccount2');
});
test('storeSyncFilterId', () async {
await database.storeSyncFilterId(
'1234',
);
final client = await database.getClient('name');
expect(client?['sync_filter_id'], '1234');
});
test('getAccountData', () async {
await database.getAccountData();
});
test('storeAccountData', () async {
await database.storeAccountData('m.test', '{"foo":"bar"}');
final events = await database.getAccountData();
expect(events.values.single.type, 'm.test');
await database.storeAccountData('m.abc+de', '{"foo":"bar"}');
final events2 = await database.getAccountData();
expect(events2.values.any((element) => element.type == 'm.abc+de'), true);
});
test('storeEventUpdate', () async {
await database.storeEventUpdate(
EventUpdate(
roomID: '!testroom:example.com',
type: EventUpdateType.timeline,
content: {
'type': EventTypes.Message,
'content': {
'body': '* edit 3',
'msgtype': 'm.text',
'm.new_content': {
'body': 'edit 3',
'msgtype': 'm.text',
},
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
},
'event_id': '\$event:example.com',
'sender': '@bob:example.org',
},
),
Client('testclient'));
});
test('getEventById', () async {
final event = await database.getEventById('\$event:example.com',
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(event?.type, EventTypes.Message);
});
test('getEventList', () async {
final events = await database.getEventList(
Room(id: '!testroom:example.com', client: Client('testclient')),
);
expect(events.single.type, EventTypes.Message);
});
test('getUser', () async {
final user = await database.getUser('@bob:example.org',
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(user, null);
});
test('getUsers', () async {
final users = await database.getUsers(
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(users.isEmpty, true);
});
test('removeEvent', () async {
await database.removeEvent(
'\$event:example.com', '!testroom:example.com');
final event = await database.getEventById('\$event:example.com',
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(event, null);
});
test('getAllInboundGroupSessions', () async {
final result = await database.getAllInboundGroupSessions();
expect(result.isEmpty, true);
});
test('getInboundGroupSession', () async {
await database.getInboundGroupSession(
'!testroom:example.com', 'sessionId');
});
test('getInboundGroupSessionsToUpload', () async {
await database.getInboundGroupSessionsToUpload();
});
test('storeInboundGroupSession', () async {
await database.storeInboundGroupSession(
'!testroom:example.com',
'sessionId',
'pickle',
'{"foo":"bar"}',
'{}',
'{}',
'senderKey',
'{}',
);
final session = await database.getInboundGroupSession(
'!testroom:example.com',
'sessionId',
);
expect(jsonDecode(session!.content)['foo'], 'bar');
});
test('markInboundGroupSessionAsUploaded', () async {
await database.markInboundGroupSessionAsUploaded(
'!testroom:example.com', 'sessionId');
});
test('markInboundGroupSessionsAsNeedingUpload', () async {
await database.markInboundGroupSessionsAsNeedingUpload();
});
test('updateInboundGroupSessionAllowedAtIndex', () async {
await database.updateInboundGroupSessionAllowedAtIndex(
'{}',
'!testroom:example.com',
'sessionId',
);
});
test('updateInboundGroupSessionIndexes', () async {
await database.updateInboundGroupSessionIndexes(
'{}',
'!testroom:example.com',
'sessionId',
);
});
test('getSSSSCache', () async {
final cache = await database.getSSSSCache('type');
expect(cache, null);
});
test('storeSSSSCache', () async {
await database.storeSSSSCache('type', 'keyId', 'ciphertext', '{}');
final cache = (await database.getSSSSCache('type'))!;
expect(cache.type, 'type');
expect(cache.keyId, 'keyId');
expect(cache.ciphertext, 'ciphertext');
expect(cache.content, '{}');
});
test('getOlmSessions', () async {
final olm = await database.getOlmSessions(
'identityKey',
'userId',
);
expect(olm.isEmpty, true);
});
test('getAllOlmSessions', () async {
var sessions = await database.getAllOlmSessions();
expect(sessions.isEmpty, true);
await database.storeOlmSession(
'identityKey',
'sessionId',
'pickle',
0,
);
await database.storeOlmSession(
'identityKey',
'sessionId2',
'pickle',
0,
);
sessions = await database.getAllOlmSessions();
expect(
sessions,
{
'identityKey': {
'sessionId': {
'identity_key': 'identityKey',
'pickle': 'pickle',
'session_id': 'sessionId',
'last_received': 0
},
'sessionId2': {
'identity_key': 'identityKey',
'pickle': 'pickle',
'session_id': 'sessionId2',
'last_received': 0
}
}
},
);
});
test('getOlmSessionsForDevices', () async {
final olm = await database.getOlmSessionsForDevices(
['identityKeys'],
'userId',
);
expect(olm.isEmpty, true);
});
test('storeOlmSession', () async {
if (!(await olmEnabled())) return;
await database.storeOlmSession(
'identityKey',
'sessionId',
'pickle',
0,
);
final olm = await database.getOlmSessions(
'identityKey',
'userId',
);
expect(olm.isNotEmpty, true);
});
test('getOutboundGroupSession', () async {
final session = await database.getOutboundGroupSession(
'!testroom:example.com',
'@alice:example.com',
);
expect(session, null);
});
test('storeOutboundGroupSession', () async {
if (!(await olmEnabled())) return;
await database.storeOutboundGroupSession(
'!testroom:example.com',
'pickle',
'{}',
0,
);
final session = await database.getOutboundGroupSession(
'!testroom:example.com',
'@alice:example.com',
);
expect(session?.devices.isEmpty, true);
});
test('getLastSentMessageUserDeviceKey', () async {
final list = await database.getLastSentMessageUserDeviceKey(
'userId',
'deviceId',
);
expect(list.isEmpty, true);
});
test('getUnimportantRoomEventStatesForRoom', () async {
final events = await database.getUnimportantRoomEventStatesForRoom(
['events'],
Room(id: '!mep', client: Client('testclient')),
);
expect(events.isEmpty, true);
});
test('getUserDeviceKeys', () async {
await database.getUserDeviceKeys(Client('testclient'));
});
test('storeUserCrossSigningKey', () async {
await database.storeUserCrossSigningKey(
'@alice:example.com',
'publicKey',
'{}',
false,
false,
);
});
test('setVerifiedUserCrossSigningKey', () async {
await database.setVerifiedUserCrossSigningKey(
true,
'@alice:example.com',
'publicKey',
);
});
test('setBlockedUserCrossSigningKey', () async {
await database.setBlockedUserCrossSigningKey(
true,
'@alice:example.com',
'publicKey',
);
});
test('removeUserCrossSigningKey', () async {
await database.removeUserCrossSigningKey(
'@alice:example.com',
'publicKey',
);
});
test('storeUserDeviceKeysInfo', () async {
await database.storeUserDeviceKeysInfo(
'@alice:example.com',
true,
);
});
test('storeUserDeviceKey', () async {
await database.storeUserDeviceKey(
'@alice:example.com',
'deviceId',
'{}',
false,
false,
0,
);
});
test('setVerifiedUserDeviceKey', () async {
await database.setVerifiedUserDeviceKey(
true,
'@alice:example.com',
'deviceId',
);
});
test('setBlockedUserDeviceKey', () async {
await database.setBlockedUserDeviceKey(
true,
'@alice:example.com',
'deviceId',
);
});
test('getStorePresences', () async {
const userId = '@alice:example.com';
final presence = CachedPresence(
PresenceType.online,
100,
'test message',
true,
'@alice:example.com',
);
await database.storePresence(
userId,
presence,
);
final storedPresence = await database.getPresence(userId);
expect(
presence.toJson(),
storedPresence?.toJson(),
);
});
// Clearing up from here
test('clearSSSSCache', () async {
await database.clearSSSSCache();
});
test('clearCache', () async {
await database.clearCache();
});
test('clear', () async {
await database.clear();
});
test('Close', () async {
await database.close();
});
}); });
} }
@ -44,451 +485,3 @@ Future<bool> olmEnabled() async {
} }
return olmEnabled; return olmEnabled;
} }
void testDatabase(
Future<DatabaseApi> futureDatabase,
) {
late DatabaseApi database;
late int toDeviceQueueIndex;
test('Open', () async {
database = await futureDatabase;
});
test('transaction', () async {
var counter = 0;
await database.transaction(() async {
expect(counter++, 0);
await database.transaction(() async {
expect(counter++, 1);
await Future.delayed(Duration(milliseconds: 50));
expect(counter++, 2);
});
expect(counter++, 3);
});
});
test('insertIntoToDeviceQueue', () async {
toDeviceQueueIndex = await database.insertIntoToDeviceQueue(
'm.test',
'txnId',
'{"foo":"bar"}',
);
});
test('getToDeviceEventQueue', () async {
final toDeviceQueue = await database.getToDeviceEventQueue();
expect(toDeviceQueue.first.type, 'm.test');
});
test('deleteFromToDeviceQueue', () async {
await database.deleteFromToDeviceQueue(toDeviceQueueIndex);
final toDeviceQueue = await database.getToDeviceEventQueue();
expect(toDeviceQueue.isEmpty, true);
});
test('storeFile', () async {
await database.storeFile(
Uri.parse('mxc://test'), Uint8List.fromList([0]), 0);
final file = await database.getFile(Uri.parse('mxc://test'));
expect(file != null, database.supportsFileStoring);
});
test('getFile', () async {
await database.getFile(Uri.parse('mxc://test'));
});
test('deleteOldFiles', () async {
await database.deleteOldFiles(1);
final file = await database.getFile(Uri.parse('mxc://test'));
expect(file == null, true);
});
test('storeRoomUpdate', () async {
final roomUpdate = JoinedRoomUpdate.fromJson({
'highlight_count': 0,
'notification_count': 0,
'limited_timeline': false,
'membership': Membership.join,
});
final client = Client('testclient');
await database.storeRoomUpdate('!testroom', roomUpdate, client);
final rooms = await database.getRoomList(client);
expect(rooms.single.id, '!testroom');
});
test('getRoomList', () async {
final room =
await database.getSingleRoom(Client('testclient'), '!testroom');
expect(room?.id, '!testroom');
});
test('getRoomList', () async {
final list = await database.getRoomList(Client('testclient'));
expect(list.single.id, '!testroom');
});
test('setRoomPrevBatch', () async {
final client = Client('testclient');
await database.setRoomPrevBatch('1234', '!testroom', client);
final rooms = await database.getRoomList(client);
expect(rooms.single.prev_batch, '1234');
});
test('forgetRoom', () async {
await database.forgetRoom('!testroom');
final rooms = await database.getRoomList(Client('testclient'));
expect(rooms.isEmpty, true);
});
test('getClient', () async {
await database.getClient('name');
});
test('insertClient', () async {
await database.insertClient(
'name',
'homeserverUrl',
'token',
'userId',
'deviceId',
'deviceName',
'prevBatch',
'olmAccount',
);
final client = await database.getClient('name');
expect(client?['token'], 'token');
});
test('updateClient', () async {
await database.updateClient(
'homeserverUrl',
'token_different',
'userId',
'deviceId',
'deviceName',
'prevBatch',
'olmAccount',
);
final client = await database.getClient('name');
expect(client?['token'], 'token_different');
});
test('updateClientKeys', () async {
await database.updateClientKeys(
'olmAccount2',
);
final client = await database.getClient('name');
expect(client?['olm_account'], 'olmAccount2');
});
test('storeSyncFilterId', () async {
await database.storeSyncFilterId(
'1234',
);
final client = await database.getClient('name');
expect(client?['sync_filter_id'], '1234');
});
test('getAccountData', () async {
await database.getAccountData();
});
test('storeAccountData', () async {
await database.storeAccountData('m.test', '{"foo":"bar"}');
final events = await database.getAccountData();
expect(events.values.single.type, 'm.test');
await database.storeAccountData('m.abc+de', '{"foo":"bar"}');
final events2 = await database.getAccountData();
expect(events2.values.any((element) => element.type == 'm.abc+de'), true);
});
test('storeEventUpdate', () async {
await database.storeEventUpdate(
EventUpdate(
roomID: '!testroom:example.com',
type: EventUpdateType.timeline,
content: {
'type': EventTypes.Message,
'content': {
'body': '* edit 3',
'msgtype': 'm.text',
'm.new_content': {
'body': 'edit 3',
'msgtype': 'm.text',
},
'm.relates_to': {
'event_id': '\$source',
'rel_type': RelationshipTypes.edit,
},
},
'event_id': '\$event:example.com',
'sender': '@bob:example.org',
},
),
Client('testclient'));
});
test('getEventById', () async {
final event = await database.getEventById('\$event:example.com',
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(event?.type, EventTypes.Message);
});
test('getEventList', () async {
final events = await database.getEventList(
Room(id: '!testroom:example.com', client: Client('testclient')),
);
expect(events.single.type, EventTypes.Message);
});
test('getUser', () async {
final user = await database.getUser('@bob:example.org',
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(user, null);
});
test('getUsers', () async {
final users = await database.getUsers(
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(users.isEmpty, true);
});
test('removeEvent', () async {
await database.removeEvent('\$event:example.com', '!testroom:example.com');
final event = await database.getEventById('\$event:example.com',
Room(id: '!testroom:example.com', client: Client('testclient')));
expect(event, null);
});
test('getAllInboundGroupSessions', () async {
final result = await database.getAllInboundGroupSessions();
expect(result.isEmpty, true);
});
test('getInboundGroupSession', () async {
await database.getInboundGroupSession('!testroom:example.com', 'sessionId');
});
test('getInboundGroupSessionsToUpload', () async {
await database.getInboundGroupSessionsToUpload();
});
test('storeInboundGroupSession', () async {
await database.storeInboundGroupSession(
'!testroom:example.com',
'sessionId',
'pickle',
'{"foo":"bar"}',
'{}',
'{}',
'senderKey',
'{}',
);
final session = await database.getInboundGroupSession(
'!testroom:example.com',
'sessionId',
);
expect(jsonDecode(session!.content)['foo'], 'bar');
});
test('markInboundGroupSessionAsUploaded', () async {
await database.markInboundGroupSessionAsUploaded(
'!testroom:example.com', 'sessionId');
});
test('markInboundGroupSessionsAsNeedingUpload', () async {
await database.markInboundGroupSessionsAsNeedingUpload();
});
test('updateInboundGroupSessionAllowedAtIndex', () async {
await database.updateInboundGroupSessionAllowedAtIndex(
'{}',
'!testroom:example.com',
'sessionId',
);
});
test('updateInboundGroupSessionIndexes', () async {
await database.updateInboundGroupSessionIndexes(
'{}',
'!testroom:example.com',
'sessionId',
);
});
test('getSSSSCache', () async {
final cache = await database.getSSSSCache('type');
expect(cache, null);
});
test('storeSSSSCache', () async {
await database.storeSSSSCache('type', 'keyId', 'ciphertext', '{}');
final cache = (await database.getSSSSCache('type'))!;
expect(cache.type, 'type');
expect(cache.keyId, 'keyId');
expect(cache.ciphertext, 'ciphertext');
expect(cache.content, '{}');
});
test('getOlmSessions', () async {
final olm = await database.getOlmSessions(
'identityKey',
'userId',
);
expect(olm.isEmpty, true);
});
test('getAllOlmSessions', () async {
var sessions = await database.getAllOlmSessions();
expect(sessions.isEmpty, true);
await database.storeOlmSession(
'identityKey',
'sessionId',
'pickle',
0,
);
await database.storeOlmSession(
'identityKey',
'sessionId2',
'pickle',
0,
);
sessions = await database.getAllOlmSessions();
expect(
sessions,
{
'identityKey': {
'sessionId': {
'identity_key': 'identityKey',
'pickle': 'pickle',
'session_id': 'sessionId',
'last_received': 0
},
'sessionId2': {
'identity_key': 'identityKey',
'pickle': 'pickle',
'session_id': 'sessionId2',
'last_received': 0
}
}
},
);
});
test('getOlmSessionsForDevices', () async {
final olm = await database.getOlmSessionsForDevices(
['identityKeys'],
'userId',
);
expect(olm.isEmpty, true);
});
test('storeOlmSession', () async {
if (!(await olmEnabled())) return;
await database.storeOlmSession(
'identityKey',
'sessionId',
'pickle',
0,
);
final olm = await database.getOlmSessions(
'identityKey',
'userId',
);
expect(olm.isNotEmpty, true);
});
test('getOutboundGroupSession', () async {
final session = await database.getOutboundGroupSession(
'!testroom:example.com',
'@alice:example.com',
);
expect(session, null);
});
test('storeOutboundGroupSession', () async {
if (!(await olmEnabled())) return;
await database.storeOutboundGroupSession(
'!testroom:example.com',
'pickle',
'{}',
0,
);
final session = await database.getOutboundGroupSession(
'!testroom:example.com',
'@alice:example.com',
);
expect(session?.devices.isEmpty, true);
});
test('getLastSentMessageUserDeviceKey', () async {
final list = await database.getLastSentMessageUserDeviceKey(
'userId',
'deviceId',
);
expect(list.isEmpty, true);
});
test('getUnimportantRoomEventStatesForRoom', () async {
final events = await database.getUnimportantRoomEventStatesForRoom(
['events'],
Room(id: '!mep', client: Client('testclient')),
);
expect(events.isEmpty, true);
});
test('getUserDeviceKeys', () async {
await database.getUserDeviceKeys(Client('testclient'));
});
test('storeUserCrossSigningKey', () async {
await database.storeUserCrossSigningKey(
'@alice:example.com',
'publicKey',
'{}',
false,
false,
);
});
test('setVerifiedUserCrossSigningKey', () async {
await database.setVerifiedUserCrossSigningKey(
true,
'@alice:example.com',
'publicKey',
);
});
test('setBlockedUserCrossSigningKey', () async {
await database.setBlockedUserCrossSigningKey(
true,
'@alice:example.com',
'publicKey',
);
});
test('removeUserCrossSigningKey', () async {
await database.removeUserCrossSigningKey(
'@alice:example.com',
'publicKey',
);
});
test('storeUserDeviceKeysInfo', () async {
await database.storeUserDeviceKeysInfo(
'@alice:example.com',
true,
);
});
test('storeUserDeviceKey', () async {
await database.storeUserDeviceKey(
'@alice:example.com',
'deviceId',
'{}',
false,
false,
0,
);
});
test('setVerifiedUserDeviceKey', () async {
await database.setVerifiedUserDeviceKey(
true,
'@alice:example.com',
'deviceId',
);
});
test('setBlockedUserDeviceKey', () async {
await database.setBlockedUserDeviceKey(
true,
'@alice:example.com',
'deviceId',
);
});
test('getStorePresences', () async {
const userId = '@alice:example.com';
final presence = CachedPresence(
PresenceType.online,
100,
'test message',
true,
'@alice:example.com',
);
await database.storePresence(
userId,
presence,
);
final storedPresence = await database.getPresence(userId);
expect(
presence.toJson(),
storedPresence?.toJson(),
);
});
// Clearing up from here
test('clearSSSSCache', () async {
await database.clearSSSSCache();
});
test('clearCache', () async {
await database.clearCache();
});
test('clear', () async {
await database.clear();
});
test('Close', () async {
await database.close();
});
return;
}

View File

@ -249,7 +249,7 @@ void main() {
test('start verification', () async { test('start verification', () async {
if (!olmEnabled) return; if (!olmEnabled) return;
var req = client var req = await client
.userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS'] .userDeviceKeys['@alice:example.com']?.deviceKeys['JLAFKJWSCS']
?.startVerification(); ?.startVerification();
expect(req != null, true); expect(req != null, true);

View File

@ -123,9 +123,9 @@ void main() {
0); 0);
// rotate after too many messages // rotate after too many messages
Iterable.generate(300).forEach((_) { for (final _ in Iterable.generate(300)) {
sess.outboundGroupSession!.encrypt('some string'); sess.outboundGroupSession!.encrypt('some string');
}); }
await client.encryption!.keyManager await client.encryption!.keyManager
.clearOrUseOutboundGroupSession(roomId); .clearOrUseOutboundGroupSession(roomId);
expect( expect(

View File

@ -260,6 +260,7 @@ class FakeMatrixApi extends BaseClient {
} }
} }
// and generate a fake sync // and generate a fake sync
// ignore: discarded_futures
_client!.handleSync(sdk.SyncUpdate(nextBatch: '')); _client!.handleSync(sdk.SyncUpdate(nextBatch: ''));
} }
return {}; return {};

View File

@ -50,7 +50,7 @@ void main() {
insertList.clear(); insertList.clear();
}); });
tearDown(() => client.dispose().onError((e, s) {})); tearDown(() async => client.dispose().onError((e, s) {}));
test('archive room not loaded', () async { test('archive room not loaded', () async {
final archiveRoom = final archiveRoom =

View File

@ -38,7 +38,7 @@ void main() {
var olmEnabled = true; var olmEnabled = true;
final countStream = StreamController<int>.broadcast(); final countStream = StreamController<int>.broadcast();
Future<int> waitForCount(int count) { Future<int> waitForCount(int count) async {
if (updateCount == count) { if (updateCount == count) {
return Future.value(updateCount); return Future.value(updateCount);
} }
@ -46,9 +46,9 @@ void main() {
final completer = Completer<int>(); final completer = Completer<int>();
StreamSubscription<int>? sub; StreamSubscription<int>? sub;
sub = countStream.stream.listen((newCount) { sub = countStream.stream.listen((newCount) async {
if (newCount == count) { if (newCount == count) {
sub?.cancel(); await sub?.cancel();
completer.complete(count); completer.complete(count);
} }
}); });
@ -100,7 +100,8 @@ void main() {
testTimeStamp = DateTime.now().millisecondsSinceEpoch; testTimeStamp = DateTime.now().millisecondsSinceEpoch;
}); });
tearDown(() => client.dispose(closeDatabase: true).onError((e, s) {})); tearDown(
() async => client.dispose(closeDatabase: true).onError((e, s) {}));
test('Request future', () async { test('Request future', () async {
timeline.events.clear(); timeline.events.clear();

View File

@ -39,7 +39,7 @@ void main() {
var currentPoison = 0; var currentPoison = 0;
final countStream = StreamController<int>.broadcast(); final countStream = StreamController<int>.broadcast();
Future<int> waitForCount(int count) { Future<int> waitForCount(int count) async {
if (updateCount == count) { if (updateCount == count) {
return Future.value(updateCount); return Future.value(updateCount);
} }
@ -47,9 +47,9 @@ void main() {
final completer = Completer<int>(); final completer = Completer<int>();
StreamSubscription<int>? sub; StreamSubscription<int>? sub;
sub = countStream.stream.listen((newCount) { sub = countStream.stream.listen((newCount) async {
if (newCount == count) { if (newCount == count) {
sub?.cancel(); await sub?.cancel();
completer.complete(count); completer.complete(count);
} }
}); });
@ -109,7 +109,8 @@ void main() {
testTimeStamp = DateTime.now().millisecondsSinceEpoch; testTimeStamp = DateTime.now().millisecondsSinceEpoch;
}); });
tearDown(() => client.dispose(closeDatabase: true).onError((e, s) {})); tearDown(
() async => client.dispose(closeDatabase: true).onError((e, s) {}));
test('Create', () async { test('Create', () async {
client.onEvent.add(EventUpdate( client.onEvent.add(EventUpdate(