Merge pull request #1934 from famedly/nico/speedup-tests

Speedup tests
This commit is contained in:
Krille-chan 2024-10-08 14:56:12 +02:00 committed by GitHub
commit 2527ea57ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 86 additions and 44 deletions

View File

@ -50,14 +50,15 @@ jobs:
retention-days: 1 retention-days: 1
coverage: coverage:
runs-on: arm-ubuntu-latest-16core #runs-on: arm-ubuntu-latest-16core
runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- run: cat .github/workflows/versions.env >> $GITHUB_ENV - run: cat .github/workflows/versions.env >> $GITHUB_ENV
- uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46 - uses: dart-lang/setup-dart@a57a6c04cf7d4840e88432aad6281d1e125f0d46
with: with:
sdk: ${{ env.dart_version }} sdk: ${{ env.dart_version }}
architecture: "arm64" #architecture: "arm64"
- name: Run tests - name: Run tests
run: | run: |
sudo apt-get update && sudo apt-get install --no-install-recommends --no-install-suggests -y lcov libsqlite3-0 libsqlite3-dev libolm3 libssl3 sudo apt-get update && sudo apt-get install --no-install-recommends --no-install-suggests -y lcov libsqlite3-0 libsqlite3-dev libolm3 libssl3

View File

@ -1,2 +1,2 @@
flutter_version=3.22.2 flutter_version=3.22.2
dart_version=3.4.3 dart_version=3.5.3

View File

@ -28,6 +28,7 @@ import 'package:matrix/encryption/utils/json_signature_check_extension.dart';
import 'package:matrix/encryption/utils/olm_session.dart'; import 'package:matrix/encryption/utils/olm_session.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
import 'package:matrix/msc_extensions/msc_3814_dehydrated_devices/api.dart'; import 'package:matrix/msc_extensions/msc_3814_dehydrated_devices/api.dart';
import 'package:matrix/src/utils/run_benchmarked.dart';
import 'package:matrix/src/utils/run_in_root.dart'; import 'package:matrix/src/utils/run_in_root.dart';
class OlmManager { class OlmManager {
@ -326,43 +327,50 @@ class OlmManager {
return false; return false;
} }
final _otkUpdateDedup = AsyncCache<void>.ephemeral();
Future<void> handleDeviceOneTimeKeysCount( Future<void> handleDeviceOneTimeKeysCount(
Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) async { Map<String, int>? countJson, List<String>? unusedFallbackKeyTypes) async {
if (!enabled) { if (!enabled) {
return; return;
} }
final haveFallbackKeys = encryption.isMinOlmVersion(3, 2, 0);
// Check if there are at least half of max_number_of_one_time_keys left on the server
// and generate and upload more if not.
// If the server did not send us a count, assume it is 0 await _otkUpdateDedup.fetch(() =>
final keyCount = countJson?.tryGet<int>('signed_curve25519') ?? 0; runBenchmarked('handleOtkUpdate', () async {
final haveFallbackKeys = encryption.isMinOlmVersion(3, 2, 0);
// Check if there are at least half of max_number_of_one_time_keys left on the server
// and generate and upload more if not.
// If the server does not support fallback keys, it will not tell us about them. // If the server did not send us a count, assume it is 0
// If the server supports them but has no key, upload a new one. final keyCount = countJson?.tryGet<int>('signed_curve25519') ?? 0;
var unusedFallbackKey = true;
if (unusedFallbackKeyTypes?.contains('signed_curve25519') == false) {
unusedFallbackKey = false;
}
// fixup accidental too many uploads. We delete only one of them so that the server has time to update the counts and because we will get rate limited anyway. // If the server does not support fallback keys, it will not tell us about them.
if (keyCount > _olmAccount!.max_number_of_one_time_keys()) { // If the server supports them but has no key, upload a new one.
final requestingKeysFrom = { var unusedFallbackKey = true;
client.userID!: {ourDeviceId!: 'signed_curve25519'} if (unusedFallbackKeyTypes?.contains('signed_curve25519') == false) {
}; unusedFallbackKey = false;
await client.claimKeys(requestingKeysFrom, timeout: 10000); }
}
// Only upload keys if they are less than half of the max or we have no unused fallback key // fixup accidental too many uploads. We delete only one of them so that the server has time to update the counts and because we will get rate limited anyway.
if (keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2) || if (keyCount > _olmAccount!.max_number_of_one_time_keys()) {
!unusedFallbackKey) { final requestingKeysFrom = {
await uploadKeys( client.userID!: {ourDeviceId!: 'signed_curve25519'}
oldKeyCount: keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2) };
? keyCount await client.claimKeys(requestingKeysFrom, timeout: 10000);
: null, }
unusedFallbackKey: haveFallbackKeys ? unusedFallbackKey : null,
); // Only upload keys if they are less than half of the max or we have no unused fallback key
} if (keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2) ||
!unusedFallbackKey) {
await uploadKeys(
oldKeyCount:
keyCount < (_olmAccount!.max_number_of_one_time_keys() / 2)
? keyCount
: null,
unusedFallbackKey: haveFallbackKeys ? unusedFallbackKey : null,
);
}
}));
} }
Future<void> storeOlmSession(OlmSession session) async { Future<void> storeOlmSession(OlmSession session) async {

View File

@ -127,10 +127,6 @@ class FakeMatrixApi extends BaseClient {
//print('\$method request to $action with Data: $data'); //print('\$method request to $action with Data: $data');
// Sync requests with timeout
if (data is Map<String, dynamic> && data['timeout'] is String) {
await Future.delayed(Duration(seconds: 5));
}
if (!servers.contains(request.url.origin)) { if (!servers.contains(request.url.origin)) {
return Response( return Response(
'<html><head></head><body>Not found ${request.url.origin}...</body></html>', '<html><head></head><body>Not found ${request.url.origin}...</body></html>',
@ -202,8 +198,19 @@ class FakeMatrixApi extends BaseClient {
'/client/v3/rooms/!1234%3AfakeServer.notExisting/state/')) { '/client/v3/rooms/!1234%3AfakeServer.notExisting/state/')) {
res = {'event_id': '\$event${_eventCounter++}'}; res = {'event_id': '\$event${_eventCounter++}'};
} else if (action.contains('/client/v3/sync')) { } else if (action.contains('/client/v3/sync')) {
// Sync requests with timeout
final timeout = request.url.queryParameters['timeout'];
if (timeout != null && timeout != '0') {
await Future.delayed(Duration(milliseconds: 50));
}
res = { res = {
'next_batch': DateTime.now().millisecondsSinceEpoch.toString(), // So that it is clear which sync we are processing prefix it with 'empty_'
'next_batch': 'empty_${DateTime.now().millisecondsSinceEpoch}',
// ensure we don't generate new keys for no reason
'device_one_time_keys_count': {
'curve25519': 10,
'signed_curve25519': 100
},
}; };
} else if (method == 'PUT' && } else if (method == 'PUT' &&
_client != null && _client != null &&
@ -1037,7 +1044,7 @@ class FakeMatrixApi extends BaseClient {
'@bob:example.com', '@bob:example.com',
], ],
}, },
'device_one_time_keys_count': {'curve25519': 10, 'signed_curve25519': 20}, 'device_one_time_keys_count': {'curve25519': 10, 'signed_curve25519': 100},
}; };
static Map<String, dynamic> archiveSyncResponse = { static Map<String, dynamic> archiveSyncResponse = {

View File

@ -16,6 +16,7 @@ void main() {
Logs().level = Level.info; Logs().level = Level.info;
setUp(() async { setUp(() async {
matrix = await getClient(); matrix = await getClient();
await matrix.abortSync();
voip = VoIP(matrix, MockWebRTCDelegate()); voip = VoIP(matrix, MockWebRTCDelegate());
VoIP.customTxid = '1234'; VoIP.customTxid = '1234';

View File

@ -45,11 +45,13 @@ void main() {
final dbPath = join(Directory.current.path, 'test.sqlite'); final dbPath = join(Directory.current.path, 'test.sqlite');
setUp(() async { setUp(() async {
expect(await File(dbPath).exists(), false); expect(await File(dbPath).exists(), false,
reason: '$dbPath should not exist');
clientOnPath = await getClient( clientOnPath = await getClient(
databasePath: dbPath, databasePath: dbPath,
); );
expect(await File(dbPath).exists(), true); await clientOnPath.abortSync();
expect(await File(dbPath).exists(), true, reason: '$dbPath should exist');
}); });
test('logout', () async { test('logout', () async {
expect(await File(dbPath).exists(), true); expect(await File(dbPath).exists(), true);
@ -137,11 +139,12 @@ void main() {
newOlmAccount: pickledOlmAccount, newOlmAccount: pickledOlmAccount,
); );
await Future.delayed(Duration(milliseconds: 50));
final loginState = await loginStateFuture; final loginState = await loginStateFuture;
final sync = await syncFuture; final sync = await syncFuture;
// to ensure our state doesn't get overwritten once we manually inject SyncUpdates
await matrix.abortSync();
expect(loginState, LoginState.loggedIn); expect(loginState, LoginState.loggedIn);
expect(matrix.onSync.value != null, true); expect(matrix.onSync.value != null, true);
expect(matrix.encryptionEnabled, true); expect(matrix.encryptionEnabled, true);
@ -185,7 +188,7 @@ void main() {
PresenceType.online); PresenceType.online);
expect(presenceCounter, 1); expect(presenceCounter, 1);
expect(accountDataCounter, 10); expect(accountDataCounter, 10);
await Future.delayed(Duration(milliseconds: 50));
expect(matrix.userDeviceKeys.keys.toSet(), { expect(matrix.userDeviceKeys.keys.toSet(), {
'@alice:example.com', '@alice:example.com',
'@othertest:fakeServer.notExisting', '@othertest:fakeServer.notExisting',
@ -1134,7 +1137,7 @@ void main() {
newOlmAccount: pickledOlmAccount, newOlmAccount: pickledOlmAccount,
); );
await Future.delayed(Duration(milliseconds: 500)); await client1.abortSync();
expect(client1.isLogged(), true); expect(client1.isLogged(), true);
expect(client1.rooms.length, 3); expect(client1.rooms.length, 3);
@ -1146,7 +1149,8 @@ void main() {
); );
await client2.init(); await client2.init();
await Future.delayed(Duration(milliseconds: 500));
await client2.abortSync();
expect(client2.isLogged(), true); expect(client2.isLogged(), true);
expect(client2.accessToken, client1.accessToken); expect(client2.accessToken, client1.accessToken);
@ -1197,6 +1201,7 @@ void main() {
await client.getWellknown(); await client.getWellknown();
expect( expect(
client.wellKnown?.mHomeserver.baseUrl.host, 'fakeserver.notexisting'); client.wellKnown?.mHomeserver.baseUrl.host, 'fakeserver.notexisting');
await client.dispose();
}); });
test('refreshAccessToken', () async { test('refreshAccessToken', () async {
@ -1204,6 +1209,7 @@ void main() {
expect(client.accessToken, 'abcd'); expect(client.accessToken, 'abcd');
await client.refreshAccessToken(); await client.refreshAccessToken();
expect(client.accessToken, 'a_new_token'); expect(client.accessToken, 'a_new_token');
await client.dispose();
}); });
test('handleSoftLogout', () async { test('handleSoftLogout', () async {
@ -1226,6 +1232,7 @@ void main() {
storedClient?.tryGet<String>('refresh_token'), storedClient?.tryGet<String>('refresh_token'),
'another_new_token', 'another_new_token',
); );
await client.dispose();
}); });
test('object equality', () async { test('object equality', () async {
@ -1274,6 +1281,7 @@ void main() {
final client = await getClient(); final client = await getClient();
client.backgroundSync = true; client.backgroundSync = true;
await client.clearCache(); await client.clearCache();
await client.dispose();
}); });
test('dispose', () async { test('dispose', () async {
@ -1311,6 +1319,7 @@ void main() {
await hiveClient.init(); await hiveClient.init();
await Future.delayed(Duration(milliseconds: 200)); await Future.delayed(Duration(milliseconds: 200));
expect(hiveClient.isLogged(), true); expect(hiveClient.isLogged(), true);
await hiveClient.dispose(closeDatabase: false);
}); });
test('getEventByPushNotification', () async { test('getEventByPushNotification', () async {
@ -1402,6 +1411,8 @@ void main() {
null, null,
true, true,
reason: '!5345234235:example.com not found as archived room'); reason: '!5345234235:example.com not found as archived room');
await client.dispose();
}); });
test( test(
@ -1430,6 +1441,7 @@ void main() {
expect(error.userId, '@user:server'); expect(error.userId, '@user:server');
expect(error.toString(), 'Exception: BAD_ACCOUNT_KEY'); expect(error.toString(), 'Exception: BAD_ACCOUNT_KEY');
} }
await customClient.dispose(closeDatabase: true);
}, },
); );

View File

@ -32,6 +32,7 @@ void main() {
test('setupClient', () async { test('setupClient', () async {
client = await getClient(); client = await getClient();
await client.abortSync();
}); });
test('fromJson', () async { test('fromJson', () async {

View File

@ -34,6 +34,7 @@ void main() {
await olm.init(); await olm.init();
olm.get_library_version(); olm.get_library_version();
client = await getClient(); client = await getClient();
await client.abortSync();
}); });
test('basic things', () async { test('basic things', () async {

View File

@ -76,6 +76,8 @@ void main() {
}); });
test('Reply To Request', () async { test('Reply To Request', () async {
final matrix = await getClient(); final matrix = await getClient();
await matrix.abortSync();
matrix.setUserId('@alice:example.com'); // we need to pretend to be alice matrix.setUserId('@alice:example.com'); // we need to pretend to be alice
FakeMatrixApi.calledEndpoints.clear(); FakeMatrixApi.calledEndpoints.clear();
await matrix await matrix

View File

@ -95,8 +95,13 @@ void main() async {
KeyVerificationMethod.reciprocate KeyVerificationMethod.reciprocate
}; };
// cancel sync to reduce background load and prevent sync overwriting which keys are tracked.
await client1.abortSync();
await client2.abortSync();
// get client2 device keys to start verification // get client2 device keys to start verification
await client1.updateUserDeviceKeys(additionalUsers: {client2.userID!}); await client1.updateUserDeviceKeys(additionalUsers: {client2.userID!});
await client2.updateUserDeviceKeys(additionalUsers: {client1.userID!});
}); });
tearDown(() async { tearDown(() async {

View File

@ -53,6 +53,7 @@ Future<Client> getClient({
newOlmAccount: pickledOlmAccount, newOlmAccount: pickledOlmAccount,
); );
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));
await client.abortSync();
return client; return client;
} }

View File

@ -55,6 +55,7 @@ void main() {
Logs().level = Level.error; Logs().level = Level.error;
test('Login', () async { test('Login', () async {
matrix = await getClient(); matrix = await getClient();
await matrix.abortSync();
}); });
test('Create from json', () async { test('Create from json', () async {

View File

@ -67,6 +67,7 @@ void main() {
client = await getClient( client = await getClient(
sendTimelineEventTimeout: const Duration(seconds: 5), sendTimelineEventTimeout: const Duration(seconds: 5),
); );
await client.abortSync();
final poison = Random().nextInt(2 ^ 32); final poison = Random().nextInt(2 ^ 32);
currentPoison = poison; currentPoison = poison;

View File

@ -48,6 +48,7 @@ void main() {
await client.login(LoginType.mLoginPassword, await client.login(LoginType.mLoginPassword,
identifier: AuthenticationUserIdentifier(user: 'test'), identifier: AuthenticationUserIdentifier(user: 'test'),
password: '1234'); password: '1234');
await client.abortSync();
}); });
tearDown(() async { tearDown(() async {
await client.logout(); await client.logout();