From 06fae391c672d7eb166ce68b66f205218b7aca0b Mon Sep 17 00:00:00 2001 From: iphydf Date: Mon, 9 Feb 2026 17:53:08 +0000 Subject: [PATCH] test: Improve testing of events parsing. --- .gitignore | 2 + lib/api/toxcore/tox_events.dart | 1973 ++++++------------ lib/widgets/chat_location_bubble.dart | 1 + pubspec.lock | 8 + pubspec.yaml | 1 + test/api/toxcore/tox_events_glados_test.dart | 451 ++++ test/mocks/fake_toxcore.dart | 24 +- test/models/id_glados_test.dart | 32 + test/models/persistence_test.dart | 55 + test/packets/message_packet_glados_test.dart | 87 + test/packets/messagepack/packer_test.dart | 203 ++ test/packets/messagepack/unpacker_test.dart | 195 ++ test/providers/tox_test.dart | 158 ++ tools/coverage_summary.sh | 53 + 14 files changed, 1904 insertions(+), 1339 deletions(-) create mode 100644 test/api/toxcore/tox_events_glados_test.dart create mode 100644 test/models/id_glados_test.dart create mode 100644 test/packets/message_packet_glados_test.dart create mode 100644 test/packets/messagepack/packer_test.dart create mode 100644 test/packets/messagepack/unpacker_test.dart create mode 100644 test/providers/tox_test.dart create mode 100755 tools/coverage_summary.sh diff --git a/.gitignore b/.gitignore index fac9c90..7eaa920 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,8 @@ migrate_working_dir/ .pub-cache/ .pub/ /build/ +/coverage/ +/devtools_options.yaml # Symbolication related app.*.symbols diff --git a/lib/api/toxcore/tox_events.dart b/lib/api/toxcore/tox_events.dart index 7d14d6e..60ea52b 100644 --- a/lib/api/toxcore/tox_events.dart +++ b/lib/api/toxcore/tox_events.dart @@ -11,1464 +11,765 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'tox_events.freezed.dart'; part 'tox_events.g.dart'; -sealed class Event extends Packet { - const Event(); - - factory Event.unpack(Unpacker unpacker, Tox_Event_Type type) { - switch (type) { - case Tox_Event_Type.TOX_EVENT_SELF_CONNECTION_STATUS: - return ToxEventSelfConnectionStatus.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_REQUEST: - return ToxEventFriendRequest.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_CONNECTION_STATUS: - return ToxEventFriendConnectionStatus.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_LOSSY_PACKET: - return ToxEventFriendLossyPacket.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_LOSSLESS_PACKET: - return ToxEventFriendLosslessPacket.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_NAME: - return ToxEventFriendName.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_STATUS: - return ToxEventFriendStatus.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_STATUS_MESSAGE: - return ToxEventFriendStatusMessage.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_MESSAGE: - return ToxEventFriendMessage.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_READ_RECEIPT: - return ToxEventFriendReadReceipt.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FRIEND_TYPING: - return ToxEventFriendTyping.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FILE_CHUNK_REQUEST: - return ToxEventFileChunkRequest.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FILE_RECV: - return ToxEventFileRecv.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FILE_RECV_CHUNK: - return ToxEventFileRecvChunk.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_FILE_RECV_CONTROL: - return ToxEventFileRecvControl.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_CONFERENCE_INVITE: - return ToxEventConferenceInvite.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_CONFERENCE_CONNECTED: - return ToxEventConferenceConnected.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED: - return ToxEventConferencePeerListChanged.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_CONFERENCE_PEER_NAME: - return ToxEventConferencePeerName.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_CONFERENCE_TITLE: - return ToxEventConferenceTitle.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_CONFERENCE_MESSAGE: - return ToxEventConferenceMessage.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_CUSTOM_PACKET: - return ToxEventGroupCustomPacket.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET: - return ToxEventGroupCustomPrivatePacket.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_INVITE: - return ToxEventGroupInvite.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PEER_JOIN: - return ToxEventGroupPeerJoin.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PEER_EXIT: - return ToxEventGroupPeerExit.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PEER_NAME: - return ToxEventGroupPeerName.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PEER_STATUS: - return ToxEventGroupPeerStatus.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_TOPIC: - return ToxEventGroupTopic.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PRIVACY_STATE: - return ToxEventGroupPrivacyState.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_VOICE_STATE: - return ToxEventGroupVoiceState.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_TOPIC_LOCK: - return ToxEventGroupTopicLock.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PEER_LIMIT: - return ToxEventGroupPeerLimit.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PASSWORD: - return ToxEventGroupPassword.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_MESSAGE: - return ToxEventGroupMessage.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_PRIVATE_MESSAGE: - return ToxEventGroupPrivateMessage.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_JOIN_FAIL: - return ToxEventGroupJoinFail.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_MODERATION: - return ToxEventGroupModeration.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_GROUP_SELF_JOIN: - return ToxEventGroupSelfJoin.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_DHT_NODES_RESPONSE: - return ToxEventDhtNodesResponse.unpack(unpacker); - case Tox_Event_Type.TOX_EVENT_INVALID: - throw Exception('Invalid event type'); - } - } - - static List unpackList(Unpacker unpacker) { - return List.unmodifiable( - List.generate(unpacker.unpackListLength(), (_) { - ensure(unpacker.unpackListLength(), 2); - return Event.unpack( - unpacker, - Tox_Event_Type.fromValue(unpacker.unpackInt()!), - ); - }), - ); - } -} - @freezed -abstract class ToxEventConferenceConnected extends Event - with _$ToxEventConferenceConnected { - const ToxEventConferenceConnected._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventConferenceConnected({required int conferenceNumber}) = - _ToxEventConferenceConnected; - - factory ToxEventConferenceConnected.fromJson(Map json) => - _$ToxEventConferenceConnectedFromJson(json); - - factory ToxEventConferenceConnected.unpack(Unpacker unpacker) { - return ToxEventConferenceConnected(conferenceNumber: unpacker.unpackInt()!); - } +sealed class Event extends Packet with _$Event { + const Event._(); - @override - void pack(Packer packer) { - packer.packInt(conferenceNumber); - } -} + const factory Event.conferenceConnected({required int conferenceNumber}) = + ToxEventConferenceConnected; -@freezed -abstract class ToxEventConferenceInvite extends Event - with _$ToxEventConferenceInvite { - const ToxEventConferenceInvite._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventConferenceInvite({ - required Uint8List cookie, + const factory Event.conferenceInvite({ + @Uint8ListConverter() required Uint8List cookie, required Tox_Conference_Type type, required int friendNumber, - }) = _ToxEventConferenceInvite; - - factory ToxEventConferenceInvite.fromJson(Map json) => - _$ToxEventConferenceInviteFromJson(json); - - factory ToxEventConferenceInvite.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventConferenceInvite( - cookie: unpacker.unpackBinary()!, - type: Tox_Conference_Type.fromValue(unpacker.unpackInt()!), - friendNumber: unpacker.unpackInt()!, - ); - } + }) = ToxEventConferenceInvite; - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packBinary(cookie) - ..packInt(type.value) - ..packInt(friendNumber); - } -} - -@freezed -abstract class ToxEventConferenceMessage extends Event - with _$ToxEventConferenceMessage { - const ToxEventConferenceMessage._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventConferenceMessage({ - required Uint8List message, + const factory Event.conferenceMessage({ + @Uint8ListConverter() required Uint8List message, required Tox_Message_Type type, required int conferenceNumber, required int peerNumber, - }) = _ToxEventConferenceMessage; + }) = ToxEventConferenceMessage; - factory ToxEventConferenceMessage.fromJson(Map json) => - _$ToxEventConferenceMessageFromJson(json); - - factory ToxEventConferenceMessage.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 4); - return ToxEventConferenceMessage( - message: unpacker.unpackBinary()!, - type: Tox_Message_Type.fromValue(unpacker.unpackInt()!), - conferenceNumber: unpacker.unpackInt()!, - peerNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(4) - ..packBinary(message) - ..packInt(type.value) - ..packInt(conferenceNumber) - ..packInt(peerNumber); - } -} - -@freezed -abstract class ToxEventConferencePeerListChanged extends Event - with _$ToxEventConferencePeerListChanged { - const ToxEventConferencePeerListChanged._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventConferencePeerListChanged({ + const factory Event.conferencePeerListChanged({ required int conferenceNumber, - }) = _ToxEventConferencePeerListChanged; - - factory ToxEventConferencePeerListChanged.fromJson( - Map json, - ) => _$ToxEventConferencePeerListChangedFromJson(json); - - factory ToxEventConferencePeerListChanged.unpack(Unpacker unpacker) { - return ToxEventConferencePeerListChanged( - conferenceNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer.packInt(conferenceNumber); - } -} + }) = ToxEventConferencePeerListChanged; -@freezed -abstract class ToxEventConferencePeerName extends Event - with _$ToxEventConferencePeerName { - const ToxEventConferencePeerName._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventConferencePeerName({ - required Uint8List name, + const factory Event.conferencePeerName({ + @Uint8ListConverter() required Uint8List name, required int conferenceNumber, required int peerNumber, - }) = _ToxEventConferencePeerName; - - factory ToxEventConferencePeerName.fromJson(Map json) => - _$ToxEventConferencePeerNameFromJson(json); - - factory ToxEventConferencePeerName.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventConferencePeerName( - name: unpacker.unpackBinary()!, - conferenceNumber: unpacker.unpackInt()!, - peerNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packBinary(name) - ..packInt(conferenceNumber) - ..packInt(peerNumber); - } -} + }) = ToxEventConferencePeerName; -@freezed -abstract class ToxEventConferenceTitle extends Event - with _$ToxEventConferenceTitle { - const ToxEventConferenceTitle._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventConferenceTitle({ - required Uint8List title, + const factory Event.conferenceTitle({ + @Uint8ListConverter() required Uint8List title, required int conferenceNumber, required int peerNumber, - }) = _ToxEventConferenceTitle; - - factory ToxEventConferenceTitle.fromJson(Map json) => - _$ToxEventConferenceTitleFromJson(json); - - factory ToxEventConferenceTitle.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventConferenceTitle( - title: unpacker.unpackBinary()!, - conferenceNumber: unpacker.unpackInt()!, - peerNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packBinary(title) - ..packInt(conferenceNumber) - ..packInt(peerNumber); - } -} + }) = ToxEventConferenceTitle; -@freezed -abstract class ToxEventDhtNodesResponse extends Event - with _$ToxEventDhtNodesResponse { - const ToxEventDhtNodesResponse._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventDhtNodesResponse({ + const factory Event.dhtNodesResponse({ required PublicKey publicKey, - required Uint8List ip, + @Uint8ListConverter() required Uint8List ip, required int port, - }) = _ToxEventDhtNodesResponse; - - factory ToxEventDhtNodesResponse.fromJson(Map json) => - _$ToxEventDhtNodesResponseFromJson(json); - - factory ToxEventDhtNodesResponse.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventDhtNodesResponse( - publicKey: PublicKey.unpack(unpacker), - ip: unpacker.unpackBinary()!, - port: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..pack(publicKey) - ..packBinary(ip) - ..packInt(port); - } -} + }) = ToxEventDhtNodesResponse; -@freezed -abstract class ToxEventFileChunkRequest extends Event - with _$ToxEventFileChunkRequest { - const ToxEventFileChunkRequest._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventFileChunkRequest({ + const factory Event.fileChunkRequest({ required int length, required int fileNumber, required int friendNumber, required int position, - }) = _ToxEventFileChunkRequest; - - factory ToxEventFileChunkRequest.fromJson(Map json) => - _$ToxEventFileChunkRequestFromJson(json); - - factory ToxEventFileChunkRequest.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 4); - return ToxEventFileChunkRequest( - length: unpacker.unpackInt()!, - fileNumber: unpacker.unpackInt()!, - friendNumber: unpacker.unpackInt()!, - position: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(4) - ..packInt(length) - ..packInt(fileNumber) - ..packInt(friendNumber) - ..packInt(position); - } -} + }) = ToxEventFileChunkRequest; -@freezed -abstract class ToxEventFileRecv extends Event with _$ToxEventFileRecv { - const ToxEventFileRecv._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFileRecv({ - required Uint8List filename, + const factory Event.fileRecv({ + @Uint8ListConverter() required Uint8List filename, required int fileNumber, required int fileSize, required int friendNumber, required int kind, - }) = _ToxEventFileRecv; - - factory ToxEventFileRecv.fromJson(Map json) => - _$ToxEventFileRecvFromJson(json); - - factory ToxEventFileRecv.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 5); - return ToxEventFileRecv( - filename: unpacker.unpackBinary()!, - fileNumber: unpacker.unpackInt()!, - fileSize: unpacker.unpackInt()!, - friendNumber: unpacker.unpackInt()!, - kind: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(5) - ..packBinary(filename) - ..packInt(fileNumber) - ..packInt(fileSize) - ..packInt(friendNumber) - ..packInt(kind); - } -} + }) = ToxEventFileRecv; -@freezed -abstract class ToxEventFileRecvChunk extends Event - with _$ToxEventFileRecvChunk { - const ToxEventFileRecvChunk._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFileRecvChunk({ - required Uint8List data, + const factory Event.fileRecvChunk({ + @Uint8ListConverter() required Uint8List data, required int fileNumber, required int friendNumber, required int position, - }) = _ToxEventFileRecvChunk; - - factory ToxEventFileRecvChunk.fromJson(Map json) => - _$ToxEventFileRecvChunkFromJson(json); - - factory ToxEventFileRecvChunk.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 4); - return ToxEventFileRecvChunk( - data: unpacker.unpackBinary()!, - fileNumber: unpacker.unpackInt()!, - friendNumber: unpacker.unpackInt()!, - position: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(4) - ..packBinary(data) - ..packInt(fileNumber) - ..packInt(friendNumber) - ..packInt(position); - } -} + }) = ToxEventFileRecvChunk; -@freezed -abstract class ToxEventFileRecvControl extends Event - with _$ToxEventFileRecvControl { - const ToxEventFileRecvControl._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventFileRecvControl({ + const factory Event.fileRecvControl({ required Tox_File_Control control, required int fileNumber, required int friendNumber, - }) = _ToxEventFileRecvControl; - - factory ToxEventFileRecvControl.fromJson(Map json) => - _$ToxEventFileRecvControlFromJson(json); - - factory ToxEventFileRecvControl.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventFileRecvControl( - control: Tox_File_Control.fromValue(unpacker.unpackInt()!), - fileNumber: unpacker.unpackInt()!, - friendNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packInt(control.value) - ..packInt(fileNumber) - ..packInt(friendNumber); - } -} + }) = ToxEventFileRecvControl; -@freezed -abstract class ToxEventFriendConnectionStatus extends Event - with _$ToxEventFriendConnectionStatus { - const ToxEventFriendConnectionStatus._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventFriendConnectionStatus({ + const factory Event.friendConnectionStatus({ required Tox_Connection connectionStatus, required int friendNumber, - }) = _ToxEventFriendConnectionStatus; - - factory ToxEventFriendConnectionStatus.fromJson(Map json) => - _$ToxEventFriendConnectionStatusFromJson(json); - - factory ToxEventFriendConnectionStatus.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventFriendConnectionStatus( - connectionStatus: Tox_Connection.fromValue(unpacker.unpackInt()!), - friendNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(connectionStatus.value) - ..packInt(friendNumber); - } -} + }) = ToxEventFriendConnectionStatus; -@freezed -abstract class ToxEventFriendLosslessPacket extends Event - with _$ToxEventFriendLosslessPacket { - const ToxEventFriendLosslessPacket._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFriendLosslessPacket({ - required Uint8List data, + const factory Event.friendLosslessPacket({ + @Uint8ListConverter() required Uint8List data, required int dataLength, required int friendNumber, - }) = _ToxEventFriendLosslessPacket; - - factory ToxEventFriendLosslessPacket.fromJson(Map json) => - _$ToxEventFriendLosslessPacketFromJson(json); - - factory ToxEventFriendLosslessPacket.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventFriendLosslessPacket( - data: unpacker.unpackBinary()!, - dataLength: unpacker.unpackInt()!, - friendNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packBinary(data) - ..packInt(dataLength) - ..packInt(friendNumber); - } -} + }) = ToxEventFriendLosslessPacket; -@freezed -abstract class ToxEventFriendLossyPacket extends Event - with _$ToxEventFriendLossyPacket { - const ToxEventFriendLossyPacket._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFriendLossyPacket({ - required Uint8List data, + const factory Event.friendLossyPacket({ + @Uint8ListConverter() required Uint8List data, required int dataLength, required int friendNumber, - }) = _ToxEventFriendLossyPacket; - - factory ToxEventFriendLossyPacket.fromJson(Map json) => - _$ToxEventFriendLossyPacketFromJson(json); - - factory ToxEventFriendLossyPacket.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventFriendLossyPacket( - data: unpacker.unpackBinary()!, - dataLength: unpacker.unpackInt()!, - friendNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packBinary(data) - ..packInt(dataLength) - ..packInt(friendNumber); - } -} + }) = ToxEventFriendLossyPacket; -@freezed -abstract class ToxEventFriendMessage extends Event - with _$ToxEventFriendMessage { - const ToxEventFriendMessage._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFriendMessage({ + const factory Event.friendMessage({ required int friendNumber, required Tox_Message_Type type, required int messageLength, - required Uint8List message, - }) = _ToxEventFriendMessage; - - factory ToxEventFriendMessage.fromJson(Map json) => - _$ToxEventFriendMessageFromJson(json); - - factory ToxEventFriendMessage.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 4); - return ToxEventFriendMessage( - friendNumber: unpacker.unpackInt()!, - type: Tox_Message_Type.fromValue(unpacker.unpackInt()!), - messageLength: unpacker.unpackInt()!, - message: unpacker.unpackBinary()!, - ); - } + @Uint8ListConverter() required Uint8List message, + }) = ToxEventFriendMessage; - @override - void pack(Packer packer) { - packer - ..packListLength(4) - ..packInt(friendNumber) - ..packInt(type.value) - ..packInt(messageLength) - ..packBinary(message); - } -} + const factory Event.friendName({ + @Uint8ListConverter() required Uint8List name, + required int friendNumber, + }) = ToxEventFriendName; -@freezed -abstract class ToxEventFriendName extends Event with _$ToxEventFriendName { - const ToxEventFriendName._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFriendName({ - required Uint8List name, + const factory Event.friendReadReceipt({ required int friendNumber, - }) = _ToxEventFriendName; + required int messageId, + }) = ToxEventFriendReadReceipt; - factory ToxEventFriendName.fromJson(Map json) => - _$ToxEventFriendNameFromJson(json); + const factory Event.friendRequest({ + @Uint8ListConverter() required Uint8List message, + required PublicKey publicKey, + }) = ToxEventFriendRequest; - factory ToxEventFriendName.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventFriendName( - name: unpacker.unpackBinary()!, - friendNumber: unpacker.unpackInt()!, - ); - } + const factory Event.friendStatus({ + required Tox_User_Status status, + required int friendNumber, + }) = ToxEventFriendStatus; - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packBinary(name) - ..packInt(friendNumber); - } -} + const factory Event.friendStatusMessage({ + @Uint8ListConverter() required Uint8List message, + required int friendNumber, + }) = ToxEventFriendStatusMessage; -@freezed -abstract class ToxEventFriendReadReceipt extends Event - with _$ToxEventFriendReadReceipt { - const ToxEventFriendReadReceipt._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventFriendReadReceipt({ + const factory Event.friendTyping({ + required bool typing, required int friendNumber, - required int messageId, - }) = _ToxEventFriendReadReceipt; + }) = ToxEventFriendTyping; - factory ToxEventFriendReadReceipt.fromJson(Map json) => - _$ToxEventFriendReadReceiptFromJson(json); + const factory Event.groupCustomPacket({ + required int groupNumber, + required int peerId, + @Uint8ListConverter() required Uint8List data, + }) = ToxEventGroupCustomPacket; - factory ToxEventFriendReadReceipt.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventFriendReadReceipt( - friendNumber: unpacker.unpackInt()!, - messageId: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(friendNumber) - ..packInt(messageId); - } -} - -@freezed -abstract class ToxEventFriendRequest extends Event - with _$ToxEventFriendRequest { - const ToxEventFriendRequest._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFriendRequest({ - required Uint8List message, - required PublicKey publicKey, - }) = _ToxEventFriendRequest; - - factory ToxEventFriendRequest.fromJson(Map json) => - _$ToxEventFriendRequestFromJson(json); - - factory ToxEventFriendRequest.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventFriendRequest( - message: unpacker.unpackBinary()!, - publicKey: PublicKey.unpack(unpacker), - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packBinary(message) - ..pack(publicKey); - } -} - -@freezed -abstract class ToxEventFriendStatus extends Event with _$ToxEventFriendStatus { - const ToxEventFriendStatus._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventFriendStatus({ - required Tox_User_Status status, - required int friendNumber, - }) = _ToxEventFriendStatus; - - factory ToxEventFriendStatus.fromJson(Map json) => - _$ToxEventFriendStatusFromJson(json); - - factory ToxEventFriendStatus.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventFriendStatus( - status: Tox_User_Status.fromValue(unpacker.unpackInt()!), - friendNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(status.value) - ..packInt(friendNumber); - } -} - -@freezed -abstract class ToxEventFriendStatusMessage extends Event - with _$ToxEventFriendStatusMessage { - const ToxEventFriendStatusMessage._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventFriendStatusMessage({ - required Uint8List message, - required int friendNumber, - }) = _ToxEventFriendStatusMessage; - - factory ToxEventFriendStatusMessage.fromJson(Map json) => - _$ToxEventFriendStatusMessageFromJson(json); - - factory ToxEventFriendStatusMessage.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventFriendStatusMessage( - message: unpacker.unpackBinary()!, - friendNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packBinary(message) - ..packInt(friendNumber); - } -} - -@freezed -abstract class ToxEventFriendTyping extends Event with _$ToxEventFriendTyping { - const ToxEventFriendTyping._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventFriendTyping({ - required bool typing, - required int friendNumber, - }) = _ToxEventFriendTyping; - - factory ToxEventFriendTyping.fromJson(Map json) => - _$ToxEventFriendTypingFromJson(json); - - factory ToxEventFriendTyping.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventFriendTyping( - typing: unpacker.unpackBool()!, - friendNumber: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packBool(typing) - ..packInt(friendNumber); - } -} - -@freezed -abstract class ToxEventGroupCustomPacket extends Event - with _$ToxEventGroupCustomPacket { - const ToxEventGroupCustomPacket._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupCustomPacket({ - required int groupNumber, - required int peerId, - required Uint8List data, - }) = _ToxEventGroupCustomPacket; - - factory ToxEventGroupCustomPacket.fromJson(Map json) => - _$ToxEventGroupCustomPacketFromJson(json); - - factory ToxEventGroupCustomPacket.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventGroupCustomPacket( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - data: unpacker.unpackBinary()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packInt(groupNumber) - ..packInt(peerId) - ..packBinary(data); - } -} - -@freezed -abstract class ToxEventGroupCustomPrivatePacket extends Event - with _$ToxEventGroupCustomPrivatePacket { - const ToxEventGroupCustomPrivatePacket._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupCustomPrivatePacket({ + const factory Event.groupCustomPrivatePacket({ required int groupNumber, required int peerId, - required Uint8List data, - }) = _ToxEventGroupCustomPrivatePacket; - - factory ToxEventGroupCustomPrivatePacket.fromJson( - Map json, - ) => _$ToxEventGroupCustomPrivatePacketFromJson(json); + @Uint8ListConverter() required Uint8List data, + }) = ToxEventGroupCustomPrivatePacket; - factory ToxEventGroupCustomPrivatePacket.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventGroupCustomPrivatePacket( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - data: unpacker.unpackBinary()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packInt(groupNumber) - ..packInt(peerId) - ..packBinary(data); - } -} - -@freezed -abstract class ToxEventGroupInvite extends Event with _$ToxEventGroupInvite { - const ToxEventGroupInvite._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupInvite({ + const factory Event.groupInvite({ required int friendNumber, - required Uint8List inviteData, - required Uint8List groupName, - }) = _ToxEventGroupInvite; + @Uint8ListConverter() required Uint8List inviteData, + @Uint8ListConverter() required Uint8List groupName, + }) = ToxEventGroupInvite; - factory ToxEventGroupInvite.fromJson(Map json) => - _$ToxEventGroupInviteFromJson(json); - - factory ToxEventGroupInvite.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventGroupInvite( - friendNumber: unpacker.unpackInt()!, - inviteData: unpacker.unpackBinary()!, - groupName: unpacker.unpackBinary()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packInt(friendNumber) - ..packBinary(inviteData) - ..packBinary(groupName); - } -} - -@freezed -abstract class ToxEventGroupJoinFail extends Event - with _$ToxEventGroupJoinFail { - const ToxEventGroupJoinFail._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupJoinFail({ + const factory Event.groupJoinFail({ required int groupNumber, required Tox_Group_Join_Fail failType, - }) = _ToxEventGroupJoinFail; + }) = ToxEventGroupJoinFail; - factory ToxEventGroupJoinFail.fromJson(Map json) => - _$ToxEventGroupJoinFailFromJson(json); - - factory ToxEventGroupJoinFail.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventGroupJoinFail( - groupNumber: unpacker.unpackInt()!, - failType: Tox_Group_Join_Fail.fromValue(unpacker.unpackInt()!), - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(groupNumber) - ..packInt(failType.value); - } -} - -@freezed -abstract class ToxEventGroupMessage extends Event with _$ToxEventGroupMessage { - const ToxEventGroupMessage._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupMessage({ + const factory Event.groupMessage({ required int groupNumber, required int peerId, required Tox_Message_Type messageType, - required Uint8List message, + @Uint8ListConverter() required Uint8List message, required int messageId, - }) = _ToxEventGroupMessage; - - factory ToxEventGroupMessage.fromJson(Map json) => - _$ToxEventGroupMessageFromJson(json); + }) = ToxEventGroupMessage; - factory ToxEventGroupMessage.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 5); - return ToxEventGroupMessage( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - messageType: Tox_Message_Type.fromValue(unpacker.unpackInt()!), - message: unpacker.unpackBinary()!, - messageId: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(5) - ..packInt(groupNumber) - ..packInt(peerId) - ..packInt(messageType.value) - ..packBinary(message) - ..packInt(messageId); - } -} - -@freezed -abstract class ToxEventGroupModeration extends Event - with _$ToxEventGroupModeration { - const ToxEventGroupModeration._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupModeration({ + const factory Event.groupModeration({ required int groupNumber, required int sourcePeerId, required int targetPeerId, required Tox_Group_Mod_Event modType, - }) = _ToxEventGroupModeration; - - factory ToxEventGroupModeration.fromJson(Map json) => - _$ToxEventGroupModerationFromJson(json); - - factory ToxEventGroupModeration.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 4); - return ToxEventGroupModeration( - groupNumber: unpacker.unpackInt()!, - sourcePeerId: unpacker.unpackInt()!, - targetPeerId: unpacker.unpackInt()!, - modType: Tox_Group_Mod_Event.fromValue(unpacker.unpackInt()!), - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(4) - ..packInt(groupNumber) - ..packInt(sourcePeerId) - ..packInt(targetPeerId) - ..packInt(modType.value); - } -} + }) = ToxEventGroupModeration; -@freezed -abstract class ToxEventGroupPassword extends Event - with _$ToxEventGroupPassword { - const ToxEventGroupPassword._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupPassword({ + const factory Event.groupPassword({ required int groupNumber, - required Uint8List password, - }) = _ToxEventGroupPassword; - - factory ToxEventGroupPassword.fromJson(Map json) => - _$ToxEventGroupPasswordFromJson(json); + @Uint8ListConverter() required Uint8List password, + }) = ToxEventGroupPassword; - factory ToxEventGroupPassword.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventGroupPassword( - groupNumber: unpacker.unpackInt()!, - password: unpacker.unpackBinary()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(groupNumber) - ..packBinary(password); - } -} - -@freezed -abstract class ToxEventGroupPeerExit extends Event - with _$ToxEventGroupPeerExit { - const ToxEventGroupPeerExit._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupPeerExit({ + const factory Event.groupPeerExit({ required int groupNumber, required int peerId, required Tox_Group_Exit_Type exitType, - required Uint8List name, - required Uint8List partMessage, - }) = _ToxEventGroupPeerExit; - - factory ToxEventGroupPeerExit.fromJson(Map json) => - _$ToxEventGroupPeerExitFromJson(json); + @Uint8ListConverter() required Uint8List name, + @Uint8ListConverter() required Uint8List partMessage, + }) = ToxEventGroupPeerExit; - factory ToxEventGroupPeerExit.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 5); - return ToxEventGroupPeerExit( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - exitType: Tox_Group_Exit_Type.fromValue(unpacker.unpackInt()!), - name: unpacker.unpackBinary()!, - partMessage: unpacker.unpackBinary()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(5) - ..packInt(groupNumber) - ..packInt(peerId) - ..packInt(exitType.value) - ..packBinary(name) - ..packBinary(partMessage); - } -} - -@freezed -abstract class ToxEventGroupPeerJoin extends Event - with _$ToxEventGroupPeerJoin { - const ToxEventGroupPeerJoin._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupPeerJoin({ + const factory Event.groupPeerJoin({ required int groupNumber, required int peerId, - }) = _ToxEventGroupPeerJoin; - - factory ToxEventGroupPeerJoin.fromJson(Map json) => - _$ToxEventGroupPeerJoinFromJson(json); + }) = ToxEventGroupPeerJoin; - factory ToxEventGroupPeerJoin.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventGroupPeerJoin( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(groupNumber) - ..packInt(peerId); - } -} - -@freezed -abstract class ToxEventGroupPeerLimit extends Event - with _$ToxEventGroupPeerLimit { - const ToxEventGroupPeerLimit._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupPeerLimit({ + const factory Event.groupPeerLimit({ required int groupNumber, required int peerLimit, - }) = _ToxEventGroupPeerLimit; - - factory ToxEventGroupPeerLimit.fromJson(Map json) => - _$ToxEventGroupPeerLimitFromJson(json); - - factory ToxEventGroupPeerLimit.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventGroupPeerLimit( - groupNumber: unpacker.unpackInt()!, - peerLimit: unpacker.unpackInt()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(groupNumber) - ..packInt(peerLimit); - } -} + }) = ToxEventGroupPeerLimit; -@freezed -abstract class ToxEventGroupPeerName extends Event - with _$ToxEventGroupPeerName { - const ToxEventGroupPeerName._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupPeerName({ + const factory Event.groupPeerName({ required int groupNumber, required int peerId, - required Uint8List name, - }) = _ToxEventGroupPeerName; - - factory ToxEventGroupPeerName.fromJson(Map json) => - _$ToxEventGroupPeerNameFromJson(json); - - factory ToxEventGroupPeerName.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventGroupPeerName( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - name: unpacker.unpackBinary()!, - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packInt(groupNumber) - ..packInt(peerId) - ..packBinary(name); - } -} + @Uint8ListConverter() required Uint8List name, + }) = ToxEventGroupPeerName; -@freezed -abstract class ToxEventGroupPeerStatus extends Event - with _$ToxEventGroupPeerStatus { - const ToxEventGroupPeerStatus._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupPeerStatus({ + const factory Event.groupPeerStatus({ required int groupNumber, required int peerId, required Tox_User_Status status, - }) = _ToxEventGroupPeerStatus; - - factory ToxEventGroupPeerStatus.fromJson(Map json) => - _$ToxEventGroupPeerStatusFromJson(json); + }) = ToxEventGroupPeerStatus; - factory ToxEventGroupPeerStatus.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventGroupPeerStatus( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - status: Tox_User_Status.fromValue(unpacker.unpackInt()!), - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(3) - ..packInt(groupNumber) - ..packInt(peerId) - ..packInt(status.value); - } -} - -@freezed -abstract class ToxEventGroupPrivacyState extends Event - with _$ToxEventGroupPrivacyState { - const ToxEventGroupPrivacyState._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupPrivacyState({ + const factory Event.groupPrivacyState({ required int groupNumber, required Tox_Group_Privacy_State privacyState, - }) = _ToxEventGroupPrivacyState; + }) = ToxEventGroupPrivacyState; - factory ToxEventGroupPrivacyState.fromJson(Map json) => - _$ToxEventGroupPrivacyStateFromJson(json); - - factory ToxEventGroupPrivacyState.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventGroupPrivacyState( - groupNumber: unpacker.unpackInt()!, - privacyState: Tox_Group_Privacy_State.fromValue(unpacker.unpackInt()!), - ); - } - - @override - void pack(Packer packer) { - packer - ..packListLength(2) - ..packInt(groupNumber) - ..packInt(privacyState.value); - } -} - -@freezed -abstract class ToxEventGroupPrivateMessage extends Event - with _$ToxEventGroupPrivateMessage { - const ToxEventGroupPrivateMessage._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupPrivateMessage({ + const factory Event.groupPrivateMessage({ required int groupNumber, required int peerId, required Tox_Message_Type messageType, - required Uint8List message, + @Uint8ListConverter() required Uint8List message, required int messageId, - }) = _ToxEventGroupPrivateMessage; - - factory ToxEventGroupPrivateMessage.fromJson(Map json) => - _$ToxEventGroupPrivateMessageFromJson(json); - - factory ToxEventGroupPrivateMessage.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 5); - return ToxEventGroupPrivateMessage( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - messageType: Tox_Message_Type.fromValue(unpacker.unpackInt()!), - message: unpacker.unpackBinary()!, - messageId: unpacker.unpackInt()!, - ); - } + }) = ToxEventGroupPrivateMessage; - @override - void pack(Packer packer) { - packer - ..packListLength(5) - ..packInt(groupNumber) - ..packInt(peerId) - ..packInt(messageType.value) - ..packBinary(message) - ..packInt(messageId); - } -} - -@freezed -abstract class ToxEventGroupSelfJoin extends Event - with _$ToxEventGroupSelfJoin { - const ToxEventGroupSelfJoin._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupSelfJoin({required int groupNumber}) = - _ToxEventGroupSelfJoin; - - factory ToxEventGroupSelfJoin.fromJson(Map json) => - _$ToxEventGroupSelfJoinFromJson(json); - - factory ToxEventGroupSelfJoin.unpack(Unpacker unpacker) { - return ToxEventGroupSelfJoin(groupNumber: unpacker.unpackInt()!); - } - - @override - void pack(Packer packer) { - packer.packInt(groupNumber); - } -} + const factory Event.groupSelfJoin({required int groupNumber}) = + ToxEventGroupSelfJoin; -@freezed -abstract class ToxEventGroupTopic extends Event with _$ToxEventGroupTopic { - const ToxEventGroupTopic._(); - @JsonSerializable( - explicitToJson: true, - fieldRename: FieldRename.snake, - converters: [Uint8ListConverter()], - ) - const factory ToxEventGroupTopic({ + const factory Event.groupTopic({ required int groupNumber, required int peerId, - required Uint8List topic, - }) = _ToxEventGroupTopic; + @Uint8ListConverter() required Uint8List topic, + }) = ToxEventGroupTopic; - factory ToxEventGroupTopic.fromJson(Map json) => - _$ToxEventGroupTopicFromJson(json); + const factory Event.groupTopicLock({ + required int groupNumber, + required Tox_Group_Topic_Lock topicLock, + }) = ToxEventGroupTopicLock; - factory ToxEventGroupTopic.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 3); - return ToxEventGroupTopic( - groupNumber: unpacker.unpackInt()!, - peerId: unpacker.unpackInt()!, - topic: unpacker.unpackBinary()!, - ); - } + const factory Event.groupVoiceState({ + required int groupNumber, + required Tox_Group_Voice_State voiceState, + }) = ToxEventGroupVoiceState; - @override - void pack(Packer packer) { - packer + const factory Event.selfConnectionStatus({ + required Tox_Connection connectionStatus, + }) = ToxEventSelfConnectionStatus; + + factory Event.fromJson(Map json) => _$EventFromJson(json); + + Tox_Event_Type get eventType => map( + conferenceConnected: (_) => Tox_Event_Type.TOX_EVENT_CONFERENCE_CONNECTED, + conferenceInvite: (_) => Tox_Event_Type.TOX_EVENT_CONFERENCE_INVITE, + conferenceMessage: (_) => Tox_Event_Type.TOX_EVENT_CONFERENCE_MESSAGE, + conferencePeerListChanged: (_) => + Tox_Event_Type.TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED, + conferencePeerName: (_) => Tox_Event_Type.TOX_EVENT_CONFERENCE_PEER_NAME, + conferenceTitle: (_) => Tox_Event_Type.TOX_EVENT_CONFERENCE_TITLE, + dhtNodesResponse: (_) => Tox_Event_Type.TOX_EVENT_DHT_NODES_RESPONSE, + fileChunkRequest: (_) => Tox_Event_Type.TOX_EVENT_FILE_CHUNK_REQUEST, + fileRecv: (_) => Tox_Event_Type.TOX_EVENT_FILE_RECV, + fileRecvChunk: (_) => Tox_Event_Type.TOX_EVENT_FILE_RECV_CHUNK, + fileRecvControl: (_) => Tox_Event_Type.TOX_EVENT_FILE_RECV_CONTROL, + friendConnectionStatus: (_) => + Tox_Event_Type.TOX_EVENT_FRIEND_CONNECTION_STATUS, + friendLosslessPacket: (_) => + Tox_Event_Type.TOX_EVENT_FRIEND_LOSSLESS_PACKET, + friendLossyPacket: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_LOSSY_PACKET, + friendMessage: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_MESSAGE, + friendName: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_NAME, + friendReadReceipt: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_READ_RECEIPT, + friendRequest: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_REQUEST, + friendStatus: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_STATUS, + friendStatusMessage: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_STATUS_MESSAGE, + friendTyping: (_) => Tox_Event_Type.TOX_EVENT_FRIEND_TYPING, + groupCustomPacket: (_) => Tox_Event_Type.TOX_EVENT_GROUP_CUSTOM_PACKET, + groupCustomPrivatePacket: (_) => + Tox_Event_Type.TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET, + groupInvite: (_) => Tox_Event_Type.TOX_EVENT_GROUP_INVITE, + groupJoinFail: (_) => Tox_Event_Type.TOX_EVENT_GROUP_JOIN_FAIL, + groupMessage: (_) => Tox_Event_Type.TOX_EVENT_GROUP_MESSAGE, + groupModeration: (_) => Tox_Event_Type.TOX_EVENT_GROUP_MODERATION, + groupPassword: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PASSWORD, + groupPeerExit: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PEER_EXIT, + groupPeerJoin: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PEER_JOIN, + groupPeerLimit: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PEER_LIMIT, + groupPeerName: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PEER_NAME, + groupPeerStatus: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PEER_STATUS, + groupPrivacyState: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PRIVACY_STATE, + groupPrivateMessage: (_) => Tox_Event_Type.TOX_EVENT_GROUP_PRIVATE_MESSAGE, + groupSelfJoin: (_) => Tox_Event_Type.TOX_EVENT_GROUP_SELF_JOIN, + groupTopic: (_) => Tox_Event_Type.TOX_EVENT_GROUP_TOPIC, + groupTopicLock: (_) => Tox_Event_Type.TOX_EVENT_GROUP_TOPIC_LOCK, + groupVoiceState: (_) => Tox_Event_Type.TOX_EVENT_GROUP_VOICE_STATE, + selfConnectionStatus: (_) => + Tox_Event_Type.TOX_EVENT_SELF_CONNECTION_STATUS, + ); + + @override + void pack(Packer packer) => when( + conferenceConnected: (conferenceNumber) => packer.packInt(conferenceNumber), + conferenceInvite: (cookie, type, friendNumber) => packer + ..packListLength(3) + ..packBinary(cookie) + ..packInt(type.value) + ..packInt(friendNumber), + conferenceMessage: (message, type, conferenceNumber, peerNumber) => packer + ..packListLength(4) + ..packBinary(message) + ..packInt(type.value) + ..packInt(conferenceNumber) + ..packInt(peerNumber), + conferencePeerListChanged: (conferenceNumber) => + packer.packInt(conferenceNumber), + conferencePeerName: (name, conferenceNumber, peerNumber) => packer + ..packListLength(3) + ..packBinary(name) + ..packInt(conferenceNumber) + ..packInt(peerNumber), + conferenceTitle: (title, conferenceNumber, peerNumber) => packer + ..packListLength(3) + ..packBinary(title) + ..packInt(conferenceNumber) + ..packInt(peerNumber), + dhtNodesResponse: (publicKey, ip, port) => packer + ..packListLength(3) + ..pack(publicKey) + ..packBinary(ip) + ..packInt(port), + fileChunkRequest: (length, fileNumber, friendNumber, position) => packer + ..packListLength(4) + ..packInt(length) + ..packInt(fileNumber) + ..packInt(friendNumber) + ..packInt(position), + fileRecv: (filename, fileNumber, fileSize, friendNumber, kind) => packer + ..packListLength(5) + ..packBinary(filename) + ..packInt(fileNumber) + ..packInt(fileSize) + ..packInt(friendNumber) + ..packInt(kind), + fileRecvChunk: (data, fileNumber, friendNumber, position) => packer + ..packListLength(4) + ..packBinary(data) + ..packInt(fileNumber) + ..packInt(friendNumber) + ..packInt(position), + fileRecvControl: (control, fileNumber, friendNumber) => packer + ..packListLength(3) + ..packInt(control.value) + ..packInt(fileNumber) + ..packInt(friendNumber), + friendConnectionStatus: (connectionStatus, friendNumber) => packer + ..packListLength(2) + ..packInt(connectionStatus.value) + ..packInt(friendNumber), + friendLosslessPacket: (data, dataLength, friendNumber) => packer + ..packListLength(3) + ..packBinary(data) + ..packInt(dataLength) + ..packInt(friendNumber), + friendLossyPacket: (data, dataLength, friendNumber) => packer + ..packListLength(3) + ..packBinary(data) + ..packInt(dataLength) + ..packInt(friendNumber), + friendMessage: (friendNumber, type, messageLength, message) => packer + ..packListLength(4) + ..packInt(friendNumber) + ..packInt(type.value) + ..packInt(messageLength) + ..packBinary(message), + friendName: (name, friendNumber) => packer + ..packListLength(2) + ..packBinary(name) + ..packInt(friendNumber), + friendReadReceipt: (friendNumber, messageId) => packer + ..packListLength(2) + ..packInt(friendNumber) + ..packInt(messageId), + friendRequest: (message, publicKey) => packer + ..packListLength(2) + ..packBinary(message) + ..pack(publicKey), + friendStatus: (status, friendNumber) => packer + ..packListLength(2) + ..packInt(status.value) + ..packInt(friendNumber), + friendStatusMessage: (message, friendNumber) => packer + ..packListLength(2) + ..packBinary(message) + ..packInt(friendNumber), + friendTyping: (typing, friendNumber) => packer + ..packListLength(2) + ..packBool(typing) + ..packInt(friendNumber), + groupCustomPacket: (groupNumber, peerId, data) => packer ..packListLength(3) ..packInt(groupNumber) ..packInt(peerId) - ..packBinary(topic); - } -} - -@freezed -abstract class ToxEventGroupTopicLock extends Event - with _$ToxEventGroupTopicLock { - const ToxEventGroupTopicLock._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupTopicLock({ - required int groupNumber, - required Tox_Group_Topic_Lock topicLock, - }) = _ToxEventGroupTopicLock; - - factory ToxEventGroupTopicLock.fromJson(Map json) => - _$ToxEventGroupTopicLockFromJson(json); - - factory ToxEventGroupTopicLock.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventGroupTopicLock( - groupNumber: unpacker.unpackInt()!, - topicLock: Tox_Group_Topic_Lock.fromValue(unpacker.unpackInt()!), - ); - } - - @override - void pack(Packer packer) { - packer + ..packBinary(data), + groupCustomPrivatePacket: (groupNumber, peerId, data) => packer + ..packListLength(3) + ..packInt(groupNumber) + ..packInt(peerId) + ..packBinary(data), + groupInvite: (friendNumber, inviteData, groupName) => packer + ..packListLength(3) + ..packInt(friendNumber) + ..packBinary(inviteData) + ..packBinary(groupName), + groupJoinFail: (groupNumber, failType) => packer ..packListLength(2) ..packInt(groupNumber) - ..packInt(topicLock.value); - } -} - -@freezed -abstract class ToxEventGroupVoiceState extends Event - with _$ToxEventGroupVoiceState { - const ToxEventGroupVoiceState._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventGroupVoiceState({ - required int groupNumber, - required Tox_Group_Voice_State voiceState, - }) = _ToxEventGroupVoiceState; - - factory ToxEventGroupVoiceState.fromJson(Map json) => - _$ToxEventGroupVoiceStateFromJson(json); - - factory ToxEventGroupVoiceState.unpack(Unpacker unpacker) { - ensure(unpacker.unpackListLength(), 2); - return ToxEventGroupVoiceState( - groupNumber: unpacker.unpackInt()!, - voiceState: Tox_Group_Voice_State.fromValue(unpacker.unpackInt()!), - ); - } - - @override - void pack(Packer packer) { - packer + ..packInt(failType.value), + groupMessage: (groupNumber, peerId, messageType, message, messageId) => + packer + ..packListLength(5) + ..packInt(groupNumber) + ..packInt(peerId) + ..packInt(messageType.value) + ..packBinary(message) + ..packInt(messageId), + groupModeration: (groupNumber, sourcePeerId, targetPeerId, modType) => + packer + ..packListLength(4) + ..packInt(groupNumber) + ..packInt(sourcePeerId) + ..packInt(targetPeerId) + ..packInt(modType.value), + groupPassword: (groupNumber, password) => packer ..packListLength(2) ..packInt(groupNumber) - ..packInt(voiceState.value); - } -} - -@freezed -abstract class ToxEventSelfConnectionStatus extends Event - with _$ToxEventSelfConnectionStatus { - const ToxEventSelfConnectionStatus._(); - @JsonSerializable(explicitToJson: true, fieldRename: FieldRename.snake) - const factory ToxEventSelfConnectionStatus({ - required Tox_Connection connectionStatus, - }) = _ToxEventSelfConnectionStatus; - - factory ToxEventSelfConnectionStatus.fromJson(Map json) => - _$ToxEventSelfConnectionStatusFromJson(json); + ..packBinary(password), + groupPeerExit: (groupNumber, peerId, exitType, name, partMessage) => packer + ..packListLength(5) + ..packInt(groupNumber) + ..packInt(peerId) + ..packInt(exitType.value) + ..packBinary(name) + ..packBinary(partMessage), + groupPeerJoin: (groupNumber, peerId) => packer + ..packListLength(2) + ..packInt(groupNumber) + ..packInt(peerId), + groupPeerLimit: (groupNumber, peerLimit) => packer + ..packListLength(2) + ..packInt(groupNumber) + ..packInt(peerLimit), + groupPeerName: (groupNumber, peerId, name) => packer + ..packListLength(3) + ..packInt(groupNumber) + ..packInt(peerId) + ..packBinary(name), + groupPeerStatus: (groupNumber, peerId, status) => packer + ..packListLength(3) + ..packInt(groupNumber) + ..packInt(peerId) + ..packInt(status.value), + groupPrivacyState: (groupNumber, privacyState) => packer + ..packListLength(2) + ..packInt(groupNumber) + ..packInt(privacyState.value), + groupPrivateMessage: + (groupNumber, peerId, messageType, message, messageId) => packer + ..packListLength(5) + ..packInt(groupNumber) + ..packInt(peerId) + ..packInt(messageType.value) + ..packBinary(message) + ..packInt(messageId), + groupSelfJoin: (groupNumber) => packer.packInt(groupNumber), + groupTopic: (groupNumber, peerId, topic) => packer + ..packListLength(3) + ..packInt(groupNumber) + ..packInt(peerId) + ..packBinary(topic), + groupTopicLock: (groupNumber, topicLock) => packer + ..packListLength(2) + ..packInt(groupNumber) + ..packInt(topicLock.value), + groupVoiceState: (groupNumber, voiceState) => packer + ..packListLength(2) + ..packInt(groupNumber) + ..packInt(voiceState.value), + selfConnectionStatus: (connectionStatus) => + packer.packInt(connectionStatus.value), + ); - factory ToxEventSelfConnectionStatus.unpack(Unpacker unpacker) { - return ToxEventSelfConnectionStatus( - connectionStatus: Tox_Connection.fromValue(unpacker.unpackInt()!), - ); + factory Event.unpack(Unpacker unpacker, Tox_Event_Type type) { + switch (type) { + case Tox_Event_Type.TOX_EVENT_SELF_CONNECTION_STATUS: + return Event.selfConnectionStatus( + connectionStatus: Tox_Connection.fromValue(unpacker.unpackInt()!), + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_REQUEST: + ensure(unpacker.unpackListLength(), 2); + return Event.friendRequest( + message: unpacker.unpackBinary()!, + publicKey: PublicKey.unpack(unpacker), + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_CONNECTION_STATUS: + ensure(unpacker.unpackListLength(), 2); + return Event.friendConnectionStatus( + connectionStatus: Tox_Connection.fromValue(unpacker.unpackInt()!), + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_LOSSY_PACKET: + ensure(unpacker.unpackListLength(), 3); + return Event.friendLossyPacket( + data: unpacker.unpackBinary()!, + dataLength: unpacker.unpackInt()!, + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_LOSSLESS_PACKET: + ensure(unpacker.unpackListLength(), 3); + return Event.friendLosslessPacket( + data: unpacker.unpackBinary()!, + dataLength: unpacker.unpackInt()!, + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_NAME: + ensure(unpacker.unpackListLength(), 2); + return Event.friendName( + name: unpacker.unpackBinary()!, + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_STATUS: + ensure(unpacker.unpackListLength(), 2); + return Event.friendStatus( + status: Tox_User_Status.fromValue(unpacker.unpackInt()!), + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_STATUS_MESSAGE: + ensure(unpacker.unpackListLength(), 2); + return Event.friendStatusMessage( + message: unpacker.unpackBinary()!, + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_MESSAGE: + ensure(unpacker.unpackListLength(), 4); + return Event.friendMessage( + friendNumber: unpacker.unpackInt()!, + type: Tox_Message_Type.fromValue(unpacker.unpackInt()!), + messageLength: unpacker.unpackInt()!, + message: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_READ_RECEIPT: + ensure(unpacker.unpackListLength(), 2); + return Event.friendReadReceipt( + friendNumber: unpacker.unpackInt()!, + messageId: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FRIEND_TYPING: + ensure(unpacker.unpackListLength(), 2); + return Event.friendTyping( + typing: unpacker.unpackBool()!, + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FILE_CHUNK_REQUEST: + ensure(unpacker.unpackListLength(), 4); + return Event.fileChunkRequest( + length: unpacker.unpackInt()!, + fileNumber: unpacker.unpackInt()!, + friendNumber: unpacker.unpackInt()!, + position: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FILE_RECV: + ensure(unpacker.unpackListLength(), 5); + return Event.fileRecv( + filename: unpacker.unpackBinary()!, + fileNumber: unpacker.unpackInt()!, + fileSize: unpacker.unpackInt()!, + friendNumber: unpacker.unpackInt()!, + kind: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FILE_RECV_CHUNK: + ensure(unpacker.unpackListLength(), 4); + return Event.fileRecvChunk( + data: unpacker.unpackBinary()!, + fileNumber: unpacker.unpackInt()!, + friendNumber: unpacker.unpackInt()!, + position: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_FILE_RECV_CONTROL: + ensure(unpacker.unpackListLength(), 3); + return Event.fileRecvControl( + control: Tox_File_Control.fromValue(unpacker.unpackInt()!), + fileNumber: unpacker.unpackInt()!, + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_CONFERENCE_INVITE: + ensure(unpacker.unpackListLength(), 3); + return Event.conferenceInvite( + cookie: unpacker.unpackBinary()!, + type: Tox_Conference_Type.fromValue(unpacker.unpackInt()!), + friendNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_CONFERENCE_CONNECTED: + return Event.conferenceConnected( + conferenceNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_CONFERENCE_PEER_LIST_CHANGED: + return Event.conferencePeerListChanged( + conferenceNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_CONFERENCE_PEER_NAME: + ensure(unpacker.unpackListLength(), 3); + return Event.conferencePeerName( + name: unpacker.unpackBinary()!, + conferenceNumber: unpacker.unpackInt()!, + peerNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_CONFERENCE_TITLE: + ensure(unpacker.unpackListLength(), 3); + return Event.conferenceTitle( + title: unpacker.unpackBinary()!, + conferenceNumber: unpacker.unpackInt()!, + peerNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_CONFERENCE_MESSAGE: + ensure(unpacker.unpackListLength(), 4); + return Event.conferenceMessage( + message: unpacker.unpackBinary()!, + type: Tox_Message_Type.fromValue(unpacker.unpackInt()!), + conferenceNumber: unpacker.unpackInt()!, + peerNumber: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_CUSTOM_PACKET: + ensure(unpacker.unpackListLength(), 3); + return Event.groupCustomPacket( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + data: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_CUSTOM_PRIVATE_PACKET: + ensure(unpacker.unpackListLength(), 3); + return Event.groupCustomPrivatePacket( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + data: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_INVITE: + ensure(unpacker.unpackListLength(), 3); + return Event.groupInvite( + friendNumber: unpacker.unpackInt()!, + inviteData: unpacker.unpackBinary()!, + groupName: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PEER_JOIN: + ensure(unpacker.unpackListLength(), 2); + return Event.groupPeerJoin( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PEER_EXIT: + ensure(unpacker.unpackListLength(), 5); + return Event.groupPeerExit( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + exitType: Tox_Group_Exit_Type.fromValue(unpacker.unpackInt()!), + name: unpacker.unpackBinary()!, + partMessage: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PEER_NAME: + ensure(unpacker.unpackListLength(), 3); + return Event.groupPeerName( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + name: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PEER_STATUS: + ensure(unpacker.unpackListLength(), 3); + return Event.groupPeerStatus( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + status: Tox_User_Status.fromValue(unpacker.unpackInt()!), + ); + case Tox_Event_Type.TOX_EVENT_GROUP_TOPIC: + ensure(unpacker.unpackListLength(), 3); + return Event.groupTopic( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + topic: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PRIVACY_STATE: + ensure(unpacker.unpackListLength(), 2); + return Event.groupPrivacyState( + groupNumber: unpacker.unpackInt()!, + privacyState: Tox_Group_Privacy_State.fromValue( + unpacker.unpackInt()!, + ), + ); + case Tox_Event_Type.TOX_EVENT_GROUP_VOICE_STATE: + ensure(unpacker.unpackListLength(), 2); + return Event.groupVoiceState( + groupNumber: unpacker.unpackInt()!, + voiceState: Tox_Group_Voice_State.fromValue(unpacker.unpackInt()!), + ); + case Tox_Event_Type.TOX_EVENT_GROUP_TOPIC_LOCK: + ensure(unpacker.unpackListLength(), 2); + return Event.groupTopicLock( + groupNumber: unpacker.unpackInt()!, + topicLock: Tox_Group_Topic_Lock.fromValue(unpacker.unpackInt()!), + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PEER_LIMIT: + ensure(unpacker.unpackListLength(), 2); + return Event.groupPeerLimit( + groupNumber: unpacker.unpackInt()!, + peerLimit: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PASSWORD: + ensure(unpacker.unpackListLength(), 2); + return Event.groupPassword( + groupNumber: unpacker.unpackInt()!, + password: unpacker.unpackBinary()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_MESSAGE: + ensure(unpacker.unpackListLength(), 5); + return Event.groupMessage( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + messageType: Tox_Message_Type.fromValue(unpacker.unpackInt()!), + message: unpacker.unpackBinary()!, + messageId: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_PRIVATE_MESSAGE: + ensure(unpacker.unpackListLength(), 5); + return Event.groupPrivateMessage( + groupNumber: unpacker.unpackInt()!, + peerId: unpacker.unpackInt()!, + messageType: Tox_Message_Type.fromValue(unpacker.unpackInt()!), + message: unpacker.unpackBinary()!, + messageId: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_GROUP_JOIN_FAIL: + ensure(unpacker.unpackListLength(), 2); + return Event.groupJoinFail( + groupNumber: unpacker.unpackInt()!, + failType: Tox_Group_Join_Fail.fromValue(unpacker.unpackInt()!), + ); + case Tox_Event_Type.TOX_EVENT_GROUP_MODERATION: + ensure(unpacker.unpackListLength(), 4); + return Event.groupModeration( + groupNumber: unpacker.unpackInt()!, + sourcePeerId: unpacker.unpackInt()!, + targetPeerId: unpacker.unpackInt()!, + modType: Tox_Group_Mod_Event.fromValue(unpacker.unpackInt()!), + ); + case Tox_Event_Type.TOX_EVENT_GROUP_SELF_JOIN: + return Event.groupSelfJoin(groupNumber: unpacker.unpackInt()!); + case Tox_Event_Type.TOX_EVENT_DHT_NODES_RESPONSE: + ensure(unpacker.unpackListLength(), 3); + return Event.dhtNodesResponse( + publicKey: PublicKey.unpack(unpacker), + ip: unpacker.unpackBinary()!, + port: unpacker.unpackInt()!, + ); + case Tox_Event_Type.TOX_EVENT_INVALID: + throw Exception('Invalid event type'); + } } - @override - void pack(Packer packer) { - packer.packInt(connectionStatus.value); + static List unpackList(Unpacker unpacker) { + return List.unmodifiable( + List.generate(unpacker.unpackListLength(), (_) { + ensure(unpacker.unpackListLength(), 2); + return Event.unpack( + unpacker, + Tox_Event_Type.fromValue(unpacker.unpackInt()!), + ); + }), + ); } } diff --git a/lib/widgets/chat_location_bubble.dart b/lib/widgets/chat_location_bubble.dart index 810a6c3..3c3b38b 100644 --- a/lib/widgets/chat_location_bubble.dart +++ b/lib/widgets/chat_location_bubble.dart @@ -51,6 +51,7 @@ final class ChatLocationBubble extends ConsumerWidget { children: [ TileLayer( urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', + userAgentPackageName: 'chat.tox.btox', tileProvider: ref.read(mapTileProvider), ), MarkerLayer( diff --git a/pubspec.lock b/pubspec.lock index 2f0aafc..81531ac 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -621,6 +621,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.5" + glados: + dependency: "direct dev" + description: + name: glados + sha256: c39446c0c4fe83a6ee6ee4fdee5a1afa27050619c7df390bf4c3b77b6f1acb33 + url: "https://pub.dev" + source: hosted + version: "1.1.7" glob: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c062dae..6cabed9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -77,6 +77,7 @@ dev_dependencies: flutter_launcher_icons: ^0.14.1 flutter_lints: ^6.0.0 freezed: ^3.2.0 + glados: ^1.1.7 json_serializable: ^6.11.0 riverpod_generator: ^4.0.0 riverpod_lint: ^3.1.0 diff --git a/test/api/toxcore/tox_events_glados_test.dart b/test/api/toxcore/tox_events_glados_test.dart new file mode 100644 index 0000000..3ae80d3 --- /dev/null +++ b/test/api/toxcore/tox_events_glados_test.dart @@ -0,0 +1,451 @@ +import 'dart:typed_data'; + +import 'package:btox/api/toxcore/tox_events.dart'; +import 'package:btox/ffi/toxcore.dart'; +import 'package:btox/models/crypto.dart'; +import 'package:btox/packets/messagepack.dart'; +import 'package:glados/glados.dart'; + +extension BToxAny on Any { + Generator get uint8List => list(uint8).map(Uint8List.fromList); + Generator uint8ListWithLength(int length) => + listWithLength(length, uint8).map(Uint8List.fromList); + + Generator get publicKey => + uint8ListWithLength(32).map(PublicKey.new); + + // Enum Generators + Generator get toxConnection => choose(Tox_Connection.values); + Generator get toxMessageType => + choose(Tox_Message_Type.values); + Generator get toxConferenceType => + choose(Tox_Conference_Type.values); + Generator get toxFileControl => + choose(Tox_File_Control.values); + Generator get toxUserStatus => + choose(Tox_User_Status.values); + Generator get toxGroupJoinFail => + choose(Tox_Group_Join_Fail.values); + Generator get toxGroupExitType => + choose(Tox_Group_Exit_Type.values); + Generator get toxGroupModEvent => + choose(Tox_Group_Mod_Event.values); + Generator get toxGroupTopicLock => + choose(Tox_Group_Topic_Lock.values); + Generator get toxGroupVoiceState => + choose(Tox_Group_Voice_State.values); + Generator get toxGroupPrivacyState => + choose(Tox_Group_Privacy_State.values); + + // Individual Event Generators + Generator get selfConnectionStatus => + toxConnection.map( + (c) => ToxEventSelfConnectionStatus(connectionStatus: c), + ); + + Generator get friendRequest => combine2( + uint8List, + publicKey, + (m, k) => ToxEventFriendRequest(message: m, publicKey: k), + ); + + Generator get friendMessage => combine3( + int32, + toxMessageType, + uint8List, + (fn, t, m) => ToxEventFriendMessage( + friendNumber: fn, + type: t, + messageLength: m.length, + message: m, + ), + ); + + Generator get dhtNodesResponse => combine3( + publicKey, + uint8List, + int32, + (k, ip, p) => ToxEventDhtNodesResponse(publicKey: k, ip: ip, port: p), + ); + + Generator get friendConnectionStatus => + combine2( + toxConnection, + int32, + (c, fn) => ToxEventFriendConnectionStatus( + connectionStatus: c, + friendNumber: fn, + ), + ); + + Generator get groupInvite => combine3( + int32, + uint8List, + uint8List, + (fn, id, gn) => + ToxEventGroupInvite(friendNumber: fn, inviteData: id, groupName: gn), + ); + + Generator get groupMessage => combine5( + int32, + int32, + toxMessageType, + uint8List, + int32, + (gn, pi, mt, m, mi) => ToxEventGroupMessage( + groupNumber: gn, + peerId: pi, + messageType: mt, + message: m, + messageId: mi, + ), + ); + + Generator get groupPeerExit => combine5( + int32, + int32, + toxGroupExitType, + uint8List, + uint8List, + (gn, pi, et, n, pm) => ToxEventGroupPeerExit( + groupNumber: gn, + peerId: pi, + exitType: et, + name: n, + partMessage: pm, + ), + ); + + Generator get conferenceConnected => + int32.map((cn) => ToxEventConferenceConnected(conferenceNumber: cn)); + + Generator get conferenceInvite => combine3( + uint8List, + toxConferenceType, + int32, + (c, t, fn) => + ToxEventConferenceInvite(cookie: c, type: t, friendNumber: fn), + ); + + Generator get conferenceMessage => combine4( + uint8List, + toxMessageType, + int32, + int32, + (m, t, cn, pn) => ToxEventConferenceMessage( + message: m, + type: t, + conferenceNumber: cn, + peerNumber: pn, + ), + ); + + Generator get conferencePeerListChanged => + int32.map( + (cn) => ToxEventConferencePeerListChanged(conferenceNumber: cn), + ); + + Generator get conferencePeerName => combine3( + uint8List, + int32, + int32, + (n, cn, pn) => ToxEventConferencePeerName( + name: n, + conferenceNumber: cn, + peerNumber: pn, + ), + ); + + Generator get conferenceTitle => combine3( + uint8List, + int32, + int32, + (t, cn, pn) => + ToxEventConferenceTitle(title: t, conferenceNumber: cn, peerNumber: pn), + ); + + Generator get fileChunkRequest => combine4( + int32, + int32, + int32, + int32, + (l, fn, frn, p) => ToxEventFileChunkRequest( + length: l, + fileNumber: fn, + friendNumber: frn, + position: p, + ), + ); + + Generator get fileRecv => combine5( + uint8List, + int32, + int32, + int32, + int32, + (n, fn, fs, frn, k) => ToxEventFileRecv( + filename: n, + fileNumber: fn, + fileSize: fs, + friendNumber: frn, + kind: k, + ), + ); + + Generator get fileRecvChunk => combine4( + uint8List, + int32, + int32, + int32, + (d, fn, frn, p) => ToxEventFileRecvChunk( + data: d, + fileNumber: fn, + friendNumber: frn, + position: p, + ), + ); + + Generator get fileRecvControl => combine3( + toxFileControl, + int32, + int32, + (c, fn, frn) => + ToxEventFileRecvControl(control: c, fileNumber: fn, friendNumber: frn), + ); + + Generator get friendLosslessPacket => combine3( + uint8List, + int32, + int32, + (d, dl, fn) => + ToxEventFriendLosslessPacket(data: d, dataLength: dl, friendNumber: fn), + ); + + Generator get friendLossyPacket => combine3( + uint8List, + int32, + int32, + (d, dl, fn) => + ToxEventFriendLossyPacket(data: d, dataLength: dl, friendNumber: fn), + ); + + Generator get friendName => combine2( + uint8List, + int32, + (n, fn) => ToxEventFriendName(name: n, friendNumber: fn), + ); + + Generator get friendReadReceipt => combine2( + int32, + int32, + (fn, mi) => ToxEventFriendReadReceipt(friendNumber: fn, messageId: mi), + ); + + Generator get friendStatus => combine2( + toxUserStatus, + int32, + (s, fn) => ToxEventFriendStatus(status: s, friendNumber: fn), + ); + + Generator get friendStatusMessage => combine2( + uint8List, + int32, + (m, fn) => ToxEventFriendStatusMessage(message: m, friendNumber: fn), + ); + + Generator get friendTyping => combine2( + this.bool, + int32, + (t, fn) => ToxEventFriendTyping(typing: t, friendNumber: fn), + ); + + Generator get groupCustomPacket => combine3( + int32, + int32, + uint8List, + (gn, pi, d) => + ToxEventGroupCustomPacket(groupNumber: gn, peerId: pi, data: d), + ); + + Generator get groupCustomPrivatePacket => + combine3( + int32, + int32, + uint8List, + (gn, pi, d) => ToxEventGroupCustomPrivatePacket( + groupNumber: gn, + peerId: pi, + data: d, + ), + ); + + Generator get groupJoinFail => combine2( + int32, + toxGroupJoinFail, + (gn, ft) => ToxEventGroupJoinFail(groupNumber: gn, failType: ft), + ); + + Generator get groupModeration => combine4( + int32, + int32, + int32, + toxGroupModEvent, + (gn, sp, tp, mt) => ToxEventGroupModeration( + groupNumber: gn, + sourcePeerId: sp, + targetPeerId: tp, + modType: mt, + ), + ); + + Generator get groupPassword => combine2( + int32, + uint8List, + (gn, p) => ToxEventGroupPassword(groupNumber: gn, password: p), + ); + + Generator get groupPeerJoin => combine2( + int32, + int32, + (gn, pi) => ToxEventGroupPeerJoin(groupNumber: gn, peerId: pi), + ); + + Generator get groupPeerLimit => combine2( + int32, + int32, + (gn, pl) => ToxEventGroupPeerLimit(groupNumber: gn, peerLimit: pl), + ); + + Generator get groupPeerName => combine3( + int32, + int32, + uint8List, + (gn, pi, n) => ToxEventGroupPeerName(groupNumber: gn, peerId: pi, name: n), + ); + + Generator get groupPeerStatus => combine3( + int32, + int32, + toxUserStatus, + (gn, pi, s) => + ToxEventGroupPeerStatus(groupNumber: gn, peerId: pi, status: s), + ); + + Generator get groupPrivacyState => combine2( + int32, + toxGroupPrivacyState, + (gn, ps) => ToxEventGroupPrivacyState(groupNumber: gn, privacyState: ps), + ); + + Generator get groupPrivateMessage => combine5( + int32, + int32, + toxMessageType, + uint8List, + int32, + (gn, pi, mt, m, mi) => ToxEventGroupPrivateMessage( + groupNumber: gn, + peerId: pi, + messageType: mt, + message: m, + messageId: mi, + ), + ); + + Generator get groupSelfJoin => + int32.map((gn) => ToxEventGroupSelfJoin(groupNumber: gn)); + + Generator get groupTopic => combine3( + int32, + int32, + uint8List, + (gn, pi, t) => ToxEventGroupTopic(groupNumber: gn, peerId: pi, topic: t), + ); + + Generator get groupTopicLock => combine2( + int32, + toxGroupTopicLock, + (gn, tl) => ToxEventGroupTopicLock(groupNumber: gn, topicLock: tl), + ); + + Generator get groupVoiceState => combine2( + int32, + toxGroupVoiceState, + (gn, vs) => ToxEventGroupVoiceState(groupNumber: gn, voiceState: vs), + ); + + // The Unified Generator + Generator get event => oneOf([ + selfConnectionStatus, + friendRequest, + friendMessage, + dhtNodesResponse, + friendConnectionStatus, + groupInvite, + groupMessage, + groupPeerExit, + conferenceConnected, + conferenceInvite, + conferenceMessage, + conferencePeerListChanged, + conferencePeerName, + conferenceTitle, + fileChunkRequest, + fileRecv, + fileRecvChunk, + fileRecvControl, + friendLosslessPacket, + friendLossyPacket, + friendName, + friendReadReceipt, + friendStatus, + friendStatusMessage, + friendTyping, + groupCustomPacket, + groupCustomPrivatePacket, + groupJoinFail, + groupModeration, + groupPassword, + groupPeerJoin, + groupPeerLimit, + groupPeerName, + groupPeerStatus, + groupPrivacyState, + groupPrivateMessage, + groupSelfJoin, + groupTopic, + groupTopicLock, + groupVoiceState, + ]); +} + +void main() { + Glados(any.event).test('Event round-trip property', (event) { + final packer = Packer(); + event.pack(packer); + final bytes = packer.takeBytes(); + + final unpacker = Unpacker(bytes); + final unpacked = Event.unpack(unpacker, event.eventType); + + expect(unpacked, event); + }); + + Glados(any.list(any.event)).test( + 'Event list round-trip property (Batch mode)', + (events) { + final packer = Packer(); + packer.packListLength(events.length); + for (final e in events) { + packer.packListLength(2); + packer.packInt(e.eventType.value); + e.pack(packer); + } + + final bytes = packer.takeBytes(); + final unpackedList = Event.unpackList(Unpacker(bytes)); + + expect(unpackedList, events); + }, + ); +} diff --git a/test/mocks/fake_toxcore.dart b/test/mocks/fake_toxcore.dart index 54352d8..49cf0ce 100644 --- a/test/mocks/fake_toxcore.dart +++ b/test/mocks/fake_toxcore.dart @@ -1,6 +1,7 @@ // Fake implementation of the Tox class for testing purposes. import 'package:btox/api/toxcore/tox.dart' as api; import 'package:btox/api/toxcore/tox_events.dart'; +import 'package:btox/ffi/toxcore.dart' as ffi; import 'package:btox/logger.dart'; import 'package:btox/models/crypto.dart'; @@ -26,15 +27,32 @@ final class FakeToxcore extends api.Tox { @override int get iterationInterval => 20; + bool throwOnBootstrap = false; + final List _events = []; + + void pushEvent(Event event) { + _events.add(event); + } + @override - void addTcpRelay(String host, int port, PublicKey publicKey) {} + void addTcpRelay(String host, int port, PublicKey publicKey) { + if (throwOnBootstrap) { + throw api.ApiException(ffi.Tox_Err_Bootstrap.TOX_ERR_BOOTSTRAP_BAD_HOST); + } + } @override - void bootstrap(String host, int port, PublicKey publicKey) {} + void bootstrap(String host, int port, PublicKey publicKey) { + if (throwOnBootstrap) { + throw api.ApiException(ffi.Tox_Err_Bootstrap.TOX_ERR_BOOTSTRAP_BAD_HOST); + } + } @override List iterate() { - return []; + final result = List.from(_events); + _events.clear(); + return result; } @override diff --git a/test/models/id_glados_test.dart b/test/models/id_glados_test.dart new file mode 100644 index 0000000..08c072d --- /dev/null +++ b/test/models/id_glados_test.dart @@ -0,0 +1,32 @@ +import 'package:btox/models/id.dart'; +import 'package:btox/models/persistence.dart'; +import 'package:glados/glados.dart'; + +void main() { + group('Id', () { + Glados(any.int).test('Id equality and hashCode', (value) { + final id1 = Id(value); + final id2 = Id(value); + final id3 = Id(value); + + expect(id1, id2); + expect(id1.hashCode, id2.hashCode); + + expect(id1, isNot(id3)); + }); + + Glados(any.int).test('Id.toString', (value) { + expect(Id(value).toString(), 'Id($value)'); + }); + }); + + group('IdConverter', () { + const converter = IdConverter(); + + Glados(any.int).test('IdConverter round-trip', (value) { + final id = Id(value); + expect(converter.fromSql(converter.toSql(id)), id); + expect(converter.fromJson(converter.toJson(id)), id); + }); + }); +} diff --git a/test/models/persistence_test.dart b/test/models/persistence_test.dart index 1593b55..54d6967 100644 --- a/test/models/persistence_test.dart +++ b/test/models/persistence_test.dart @@ -83,6 +83,61 @@ void main() { ); }, tags: ['models']); + testDatabase('Profile lifecycle and settings', (db) async { + final p1 = await db.addProfile( + ProfilesCompanion.insert( + active: Value(true), + settings: ProfileSettings(nickname: 'P1', statusMessage: ''), + secretKey: mySecretKey, + publicKey: myToxId.publicKey, + nospam: myToxId.nospam, + ), + ); + final p2 = await db.addProfile( + ProfilesCompanion.insert( + active: Value(false), + settings: ProfileSettings(nickname: 'P2', statusMessage: ''), + secretKey: mySecretKey, + publicKey: myToxId.publicKey, + nospam: myToxId.nospam, + ), + ); + + // Initial state + expect((await db.watchProfile(p1).first).active, isTrue); + expect((await db.watchProfile(p2).first).active, isFalse); + + // Activate P2 + await db.activateProfile(p2); + expect((await db.watchProfile(p1).first).active, isFalse); + expect((await db.watchProfile(p2).first).active, isTrue); + + // Update settings + final newSettings = ProfileSettings( + nickname: 'NewName', + statusMessage: 'Busy', + ); + await db.updateProfileSettings(p2, newSettings); + expect((await db.watchProfile(p2).first).settings, newSettings); + + // Delete P2 and verify cascade + await db.addContact( + ContactsCompanion.insert( + profileId: p2, + name: Value('Contact'), + publicKey: friendPk, + ), + ); + await db.deleteProfile(p2); + + final allProfiles = await db.watchProfiles().first; + expect(allProfiles.length, 1); + expect(allProfiles.first.id, p1); + + final contacts = await db.watchContactsFor(p2).first; + expect(contacts, isEmpty); + }, tags: ['models']); + testDatabase('Two separate histories can be merged', (db) async { final profileId = await db.addProfile( ProfilesCompanion.insert( diff --git a/test/packets/message_packet_glados_test.dart b/test/packets/message_packet_glados_test.dart new file mode 100644 index 0000000..c540509 --- /dev/null +++ b/test/packets/message_packet_glados_test.dart @@ -0,0 +1,87 @@ +import 'package:btox/models/content.dart'; +import 'package:btox/models/crypto.dart'; +import 'package:btox/packets/messagepack.dart'; +import 'package:btox/packets/message_packet.dart'; +import 'package:glados/glados.dart'; + +import '../api/toxcore/tox_events_glados_test.dart'; + +extension MessagePacketAny on Any { + Generator get sha256 => uint8ListWithLength(32).map(Sha256.new); + + Generator get textContent => + letters.map((t) => TextContent(text: t)); + + Generator get deleteContent => + sha256.map((s) => DeleteContent(message: s)); + + Generator get editContent => + combine2(sha256, letters, (s, t) => EditContent(message: s, text: t)); + + Generator get fileContent => + letters.map((u) => FileContent(url: u)); + + Generator get mediaContent => + letters.map((u) => MediaContent(url: u)); + + Generator get locationContent => combine2( + this.double, + this.double, + (lat, lon) => LocationContent(latitude: lat, longitude: lon), + ); + + Generator get reactionContent => combine2( + sha256, + letters, + (s, e) => ReactionContent(message: s, emoji: e), + ); + + Generator get content => oneOf([ + textContent, + deleteContent, + editContent, + fileContent, + mediaContent, + locationContent, + reactionContent, + ]); + + Generator get messagePacket => combine5( + sha256.nullable, + sha256.nullable, + dateTime, + publicKey, + content, + (p, m, t, a, c) => MessagePacket( + parent: p, + merged: m, + timestamp: t, + author: a, + content: c, + ), + ); +} + +void main() { + Glados(any.messagePacket).test('MessagePacket round-trip property', (packet) { + final encoded = packet.encode(); + final decoded = MessagePacket.unpack(Unpacker(encoded)); + + expect(decoded.parent, packet.parent); + expect(decoded.merged, packet.merged); + // Precision might be lost in DateTime depending on how it's packed (ms vs us). + // MessagePacket uses millisecondsSinceEpoch. + expect( + decoded.timestamp.millisecondsSinceEpoch, + packet.timestamp.millisecondsSinceEpoch, + ); + expect(decoded.author, packet.author); + expect(decoded.content, packet.content); + }); + + Glados(any.content).test('Content round-trip property', (content) { + final encoded = content.encode(); + final decoded = Content.unpack(Unpacker(encoded)); + expect(decoded, content); + }); +} diff --git a/test/packets/messagepack/packer_test.dart b/test/packets/messagepack/packer_test.dart new file mode 100644 index 0000000..32a5fb4 --- /dev/null +++ b/test/packets/messagepack/packer_test.dart @@ -0,0 +1,203 @@ +import 'dart:typed_data'; +import 'package:btox/packets/messagepack/packer.dart'; +import 'package:btox/packets/messagepack/tags.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Packer', () { + test('packInt handles all ranges', () { + void check(int value, List expected) { + final p = Packer()..packInt(value); + expect( + p.takeBytes(), + Uint8List.fromList(expected), + reason: 'Value $value', + ); + } + + // Positive fixint + check(0, [0x00]); + check(127, [0x7f]); + + // uint8 + check(128, [kTagUint8, 0x80]); + check(255, [kTagUint8, 0xff]); + + // uint16 + check(256, [kTagUint16, 0x01, 0x00]); + check(65535, [kTagUint16, 0xff, 0xff]); + + // uint32 + check(65536, [kTagUint32, 0x00, 0x01, 0x00, 0x00]); + check(4294967295, [kTagUint32, 0xff, 0xff, 0xff, 0xff]); + + // uint64 + check(4294967296, [ + kTagUint64, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x00, + ]); + + // Negative fixint + check(-1, [0xff]); + check(-32, [0xe0]); + + // int8 + check(-33, [kTagInt8, 0xdf]); + check(-128, [kTagInt8, 0x80]); + + // int16 + check(-129, [kTagInt16, 0xff, 0x7f]); + check(-32768, [kTagInt16, 0x80, 0x00]); + + // int32 + check(-32769, [kTagInt32, 0xff, 0xff, 0x7f, 0xff]); + check(-2147483648, [kTagInt32, 0x80, 0x00, 0x00, 0x00]); + + // int64 + check(-2147483649, [ + kTagInt64, + 0xff, + 0xff, + 0xff, + 0xff, + 0x7f, + 0xff, + 0xff, + 0xff, + ]); + }); + + test('packString handles different lengths', () { + void check(int length, int tag, [int? lengthBytes]) { + final s = 'a' * length; + final p = Packer()..packString(s); + final bytes = p.takeBytes(); + expect(bytes[0], tag); + expect(bytes.length, 1 + (lengthBytes ?? 0) + length); + } + + check(31, 0xA0 | 31); + check(32, kTagStr8, 1); + check(255, kTagStr8, 1); + check(256, kTagStr16, 2); + check(65535, kTagStr16, 2); + check(65536, kTagStr32, 4); + }); + + test('packBinary handles different lengths', () { + void check(int length, int tag, int lengthBytes) { + final b = Uint8List(length); + final p = Packer()..packBinary(b); + final bytes = p.takeBytes(); + expect(bytes[0], tag); + expect(bytes.length, 1 + lengthBytes + length); + } + + check(255, kTagBin8, 1); + check(256, kTagBin16, 2); + check(65535, kTagBin16, 2); + check(65536, kTagBin32, 4); + }); + + test('packListLength handles different lengths', () { + void check(int length, int tag, [int? lengthBytes]) { + final p = Packer()..packListLength(length); + final bytes = p.takeBytes(); + expect(bytes[0], tag); + if (lengthBytes != null) { + expect(bytes.length, 1 + lengthBytes); + } + } + + check(15, 0x90 | 15); + check(16, kTagArray16, 2); + check(65535, kTagArray16, 2); + check(65536, kTagArray32, 4); + }); + + test('packMapLength handles different lengths', () { + void check(int length, int tag, [int? lengthBytes]) { + final p = Packer()..packMapLength(length); + final bytes = p.takeBytes(); + expect(bytes[0], tag); + if (lengthBytes != null) { + expect(bytes.length, 1 + lengthBytes); + } + } + + check(15, 0x80 | 15); + check(16, kTagMap16, 2); + check(65535, kTagMap16, 2); + check(65536, kTagMap32, 4); + }); + + test('Packer buffer resizing', () { + final p = Packer(4); + p.packBinary(Uint8List(10)); + expect(p.takeBytes().length, 12); + }); + + test('packBool', () { + expect( + (Packer()..packBool(true)).takeBytes(), + Uint8List.fromList([kTagTrue]), + ); + expect( + (Packer()..packBool(false)).takeBytes(), + Uint8List.fromList([kTagFalse]), + ); + expect( + (Packer()..packBool(null)).takeBytes(), + Uint8List.fromList([kTagNil]), + ); + }); + + test('packDouble', () { + expect( + (Packer()..packDouble(null)).takeBytes(), + Uint8List.fromList([kTagNil]), + ); + // 3.14 in float64 is 40 09 1e b8 51 eb 85 1f + expect( + (Packer()..packDouble(3.14)).takeBytes(), + Uint8List.fromList([ + kTagFloat64, + 0x40, + 0x09, + 0x1e, + 0xb8, + 0x51, + 0xeb, + 0x85, + 0x1f, + ]), + ); + }); + + test('packNull', () { + expect((Packer()..packNull()).takeBytes(), Uint8List.fromList([kTagNil])); + }); + + test('packStringEmptyIsNull', () { + expect( + (Packer()..packStringEmptyIsNull('')).takeBytes(), + Uint8List.fromList([kTagNil]), + ); + expect( + (Packer()..packStringEmptyIsNull(null)).takeBytes(), + Uint8List.fromList([kTagNil]), + ); + expect( + (Packer()..packStringEmptyIsNull('a')).takeBytes(), + Uint8List.fromList([0xA1, 0x61]), + ); + }); + }); +} diff --git a/test/packets/messagepack/unpacker_test.dart b/test/packets/messagepack/unpacker_test.dart new file mode 100644 index 0000000..61bd75b --- /dev/null +++ b/test/packets/messagepack/unpacker_test.dart @@ -0,0 +1,195 @@ +import 'dart:typed_data'; +import 'package:btox/packets/messagepack/message_data.dart'; +import 'package:btox/packets/messagepack/packer.dart'; +import 'package:btox/packets/messagepack/unpacker.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('Unpacker', () { + test('unpackInt handles all ranges', () { + void check(int? value) { + final bytes = (Packer()..packInt(value)).takeBytes(); + final result = Unpacker(bytes).unpackInt(); + expect(result, value, reason: 'Value $value'); + } + + check(0); + check(127); + check(128); + check(255); + check(256); + check(65535); + check(65536); + check(4294967295); + check(4294967296); + check(-1); + check(-32); + check(-33); + check(-128); + check(-129); + check(-32768); + check(-32769); + check(-2147483648); + check(-2147483649); + check(null); // Test null + }); + + test('unpackString handles different lengths', () { + void check(int length) { + final s = 'a' * length; + final bytes = (Packer()..packString(s)).takeBytes(); + final result = Unpacker(bytes).unpackString(); + expect(result, s, reason: 'Length $length'); + } + + check(31); + check(32); + check(255); + check(256); + check(65535); + check(65536); + expect( + Unpacker((Packer()..packString(null)).takeBytes()).unpackString(), + isNull, + ); + }); + + test('unpackBinary handles different lengths', () { + void check(int length) { + final b = Uint8List(length); + final bytes = (Packer()..packBinary(b)).takeBytes(); + final result = Unpacker(bytes).unpackBinary(); + expect(result, b, reason: 'Length $length'); + } + + check(255); + check(256); + check(65535); + check(65536); + expect( + Unpacker((Packer()..packBinary(null)).takeBytes()).unpackBinary(), + isNull, + ); + }); + + test('unpackListLength handles different lengths', () { + void check(int length) { + final bytes = (Packer()..packListLength(length)).takeBytes(); + final result = Unpacker(bytes).unpackListLength(); + expect(result, length, reason: 'Length $length'); + } + + check(15); + check(16); + check(65535); + check(65536); + expect( + Unpacker( + (Packer()..packListLength(null)).takeBytes(), + ).unpackListLength(), + 0, + ); + }); + + test('unpackMapLength handles different lengths', () { + void check(int length) { + final bytes = (Packer()..packMapLength(length)).takeBytes(); + final result = Unpacker(bytes).unpackMapLength(); + expect(result, length, reason: 'Length $length'); + } + + check(15); + check(16); + check(65535); + check(65536); + expect( + Unpacker((Packer()..packMapLength(null)).takeBytes()).unpackMapLength(), + 0, + ); + }); + + test('unpackBool', () { + expect( + Unpacker((Packer()..packBool(true)).takeBytes()).unpackBool(), + isTrue, + ); + expect( + Unpacker((Packer()..packBool(false)).takeBytes()).unpackBool(), + isFalse, + ); + expect( + Unpacker((Packer()..packBool(null)).takeBytes()).unpackBool(), + isNull, + ); + }); + + test('unpackDouble', () { + expect( + Unpacker((Packer()..packDouble(3.14)).takeBytes()).unpackDouble(), + 3.14, + ); + expect( + Unpacker((Packer()..packDouble(null)).takeBytes()).unpackDouble(), + isNull, + ); + }); + + test('unpackNull', () { + Unpacker((Packer()..packNull()).takeBytes()).unpackNull(); + }); + + test('FormatExceptions', () { + final bytes = (Packer()..packString('not an int')).takeBytes(); + expect(() => Unpacker(bytes).unpackInt(), throwsFormatException); + expect(() => Unpacker(bytes).unpackBool(), throwsFormatException); + expect(() => Unpacker(bytes).unpackBinary(), throwsFormatException); + }); + + test('unpack MessageData types', () { + final packer = Packer(); + packer.packInt(123); + packer.packBool(true); + packer.packDouble(1.23); + packer.packString('test'); + packer.packBinary(Uint8List.fromList([1, 2, 3])); + packer.packNull(); + + final unpacker = Unpacker(packer.takeBytes()); + expect((unpacker.unpack() as MessageDataInt).value, 123); + expect((unpacker.unpack() as MessageDataBool).value, true); + expect( + (unpacker.unpack() as MessageDataDouble).value, + closeTo(1.23, 0.001), + ); + expect((unpacker.unpack() as MessageDataString).value, 'test'); + expect( + (unpacker.unpack() as MessageDataBinary).value, + Uint8List.fromList([1, 2, 3]), + ); + expect(unpacker.unpack(), isA()); + }); + + test('unpackList and unpackMap', () { + final packer = Packer(); + packer.packListLength(2); + packer.packInt(1); + packer.packInt(2); + + packer.packMapLength(1); + packer.packString('key'); + packer.packString('value'); + + final unpacker = Unpacker(packer.takeBytes()); + final list = unpacker.unpackList(); + expect(list.length, 2); + expect((list[0] as MessageDataInt).value, 1); + expect((list[1] as MessageDataInt).value, 2); + + final map = unpacker.unpackMap(); + expect(map.length, 1); + final entry = map.entries.first; + expect((entry.key as MessageDataString).value, 'key'); + expect((entry.value as MessageDataString).value, 'value'); + }); + }); +} diff --git a/test/providers/tox_test.dart b/test/providers/tox_test.dart new file mode 100644 index 0000000..3c75dac --- /dev/null +++ b/test/providers/tox_test.dart @@ -0,0 +1,158 @@ +import 'dart:typed_data'; +import 'package:btox/api/toxcore/tox_events.dart'; +import 'package:btox/ffi/toxcore.dart' as ffi; +import 'package:btox/models/bootstrap_nodes.dart'; +import 'package:btox/models/crypto.dart'; +import 'package:btox/providers/bootstrap_nodes.dart'; +import 'package:btox/providers/tox.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_test/flutter_test.dart'; +import '../mocks/fake_toxcore.dart'; + +void main() { + test('toxEvents yields events from tox.iterate()', () async { + final fakeTox = FakeToxcore(); + final secretKey = SecretKey(Uint8List(32)); + final nospam = ToxAddressNospam(0); + + final container = ProviderContainer( + overrides: [ + bootstrapNodesProvider.overrideWith( + (ref) => BootstrapNodeList( + nodes: [ + BootstrapNode( + ipv4: '127.0.0.1', + port: 33445, + publicKey: PublicKey(Uint8List(32)), + tcpPorts: [33446], + ipv6: '', + maintainer: '', + location: '', + statusUdp: true, + statusTcp: true, + version: '', + motd: '', + lastPing: 0, + ), + ], + lastRefresh: 0, + lastScan: 0, + ), + ), + toxProvider(secretKey, nospam).overrideWith((ref) => fakeTox), + ], + ); + + final events = []; + final subscription = container.listen( + toxEventsProvider(secretKey, nospam), + (previous, next) { + next.whenData((event) => events.add(event)); + }, + fireImmediately: true, + ); + + final event = ToxEventSelfConnectionStatus( + connectionStatus: ffi.Tox_Connection.TOX_CONNECTION_UDP, + ); + fakeTox.pushEvent(event); + + // Give it a bit of time to iterate + await Future.delayed(const Duration(milliseconds: 50)); + + expect(events, contains(event)); + + subscription.close(); + fakeTox.kill(); + }); + + test('toxEvents handles bootstrap failure gracefully', () async { + final fakeTox = FakeToxcore()..throwOnBootstrap = true; + final secretKey = SecretKey(Uint8List(32)); + final nospam = ToxAddressNospam(0); + + final container = ProviderContainer( + overrides: [ + bootstrapNodesProvider.overrideWith( + (ref) => BootstrapNodeList( + nodes: [ + BootstrapNode( + ipv4: '127.0.0.1', + port: 33445, + publicKey: PublicKey(Uint8List(32)), + tcpPorts: [33446], + ipv6: '', + maintainer: '', + location: '', + statusUdp: true, + statusTcp: true, + version: '', + motd: '', + lastPing: 0, + ), + ], + lastRefresh: 0, + lastScan: 0, + ), + ), + toxProvider(secretKey, nospam).overrideWith((ref) => fakeTox), + ], + ); + + final events = []; + final subscription = container.listen( + toxEventsProvider(secretKey, nospam), + (previous, next) { + next.whenData((event) => events.add(event)); + }, + ); + + fakeTox.kill(); + await Future.delayed(const Duration(milliseconds: 50)); + subscription.close(); + }); + + test('toxEvents ignores ToxEventDhtNodesResponse', () async { + final fakeTox = FakeToxcore(); + final secretKey = SecretKey(Uint8List(32)); + final nospam = ToxAddressNospam(0); + + final container = ProviderContainer( + overrides: [ + bootstrapNodesProvider.overrideWith( + (ref) => BootstrapNodeList(nodes: [], lastRefresh: 0, lastScan: 0), + ), + toxProvider(secretKey, nospam).overrideWith((ref) => fakeTox), + ], + ); + + final events = []; + final subscription = container.listen( + toxEventsProvider(secretKey, nospam), + (previous, next) { + next.whenData((event) => events.add(event)); + }, + ); + + fakeTox.pushEvent( + ToxEventDhtNodesResponse( + publicKey: PublicKey(Uint8List(32)), + ip: Uint8List.fromList([127, 0, 0, 1]), + port: 33445, + ), + ); + + final event = ToxEventSelfConnectionStatus( + connectionStatus: ffi.Tox_Connection.TOX_CONNECTION_UDP, + ); + fakeTox.pushEvent(event); + + await Future.delayed(const Duration(milliseconds: 50)); + + expect(events, isNot(contains(isA()))); + expect(events, contains(event)); + + subscription.close(); + fakeTox.kill(); + }); +} diff --git a/tools/coverage_summary.sh b/tools/coverage_summary.sh new file mode 100755 index 0000000..9e90bbd --- /dev/null +++ b/tools/coverage_summary.sh @@ -0,0 +1,53 @@ +#!/bin/bash + +# This script generates a coverage summary from coverage/lcov.info +# It excludes generated files (.g.dart, .freezed.dart, and generated/ directories) + +LCOV_FILE="coverage/lcov.info" + +if [ ! -f "$LCOV_FILE" ]; then + echo "Error: $LCOV_FILE not found. Run 'flutter test --coverage' first." + exit 1 +fi + +echo "Per-file coverage (excluding generated files):" +echo "------------------------------------------------" + +# Per-file summary +awk -F: ' + /^SF:/ { file = $2; } + /^LH:/ { lh[file] = $2; } + /^LF:/ { lf[file] = $2; } + END { + for (f in lf) { + if (f !~ /(\.g\.dart|\.freezed\.dart|generated\/)/) { + if (lf[f] > 0) { + printf "%6.2f%% | %s\n", (lh[f]/lf[f])*100, f; + } + } + } + }' "$LCOV_FILE" | sort -n + +echo "------------------------------------------------" +echo "Core Logic Total Coverage:" + +# Total summary +awk ' + BEGIN { RS = "end_of_record\n" } + !/SF:.*(\.g\.dart|\.freezed\.dart|generated\/)/ { + split($0, lines, "\n"); + for (i in lines) { + if (lines[i] ~ /^LH:/) { + split(lines[i], parts, ":"); + lh += parts[2]; + } + if (lines[i] ~ /^LF:/) { + split(lines[i], parts, ":"); + lf += parts[2]; + } + } + } + END { + if (lf > 0) printf "Lines Hit: %d, Total Lines: %d, Coverage: %.2f%%\n", lh, lf, (lh/lf)*100; + else print "No lines found"; + }' "$LCOV_FILE"