Skip to content

Commit

Permalink
Add reconnection
Browse files Browse the repository at this point in the history
  • Loading branch information
PlugFox committed May 23, 2024
1 parent 7e47f7e commit aba9baf
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 35 deletions.
4 changes: 2 additions & 2 deletions lib/src/model/transport_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ typedef SpinifyTransportBuilder = Future<ISpinifyTransport> Function({
required SpinifyMetrics$Mutable metrics,

/// Callback for reply messages
required void Function(SpinifyReply reply) onReply,
required Future<void> Function(SpinifyReply reply) onReply,

/// Callback for disconnect event
required void Function() onDisconnect,
required Future<void> Function() onDisconnect,
});

/// Spinify transport interface.
Expand Down
2 changes: 1 addition & 1 deletion lib/src/spinify_impl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ base mixin SpinifyConnectionMixin
if (state.url == url) return;
final completer = _readyCompleter ??= Completer<void>();
try {
await disconnect();
if (state.isConnected || state.isConnecting) await disconnect();
} on Object {/* ignore */}
try {
_setState(SpinifyState$Connecting(url: _metrics.reconnectUrl = url));
Expand Down
64 changes: 34 additions & 30 deletions lib/src/transport_fake.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,38 @@ import 'dart:async';
import 'package:fixnum/fixnum.dart';

import 'model/command.dart';
import 'model/config.dart';
import 'model/metric.dart';
import 'model/reply.dart';
import 'model/transport_interface.dart';

/// Create a fake Spinify transport.
Future<ISpinifyTransport> $createFakeSpinifyTransport({
/// URL for the connection
required String url,

/// Spinify client configuration
required SpinifyConfig config,

/// Metrics
required SpinifyMetrics$Mutable metrics,

/// Callback for reply messages
required void Function(SpinifyReply reply) onReply,

/// Callback for disconnect event
required void Function() onDisconnect,
}) async {
final transport = SpinifyTransportFake()
..metrics = metrics
..onReply = onReply
..onDisconnect = onDisconnect;
await transport._connect(url);
return transport;
}
SpinifyTransportBuilder $createFakeSpinifyTransport([
void Function(ISpinifyTransport transport)? out,
]) =>
({
/// URL for the connection
required url,

/// Spinify client configuration
required config,

/// Metrics
required metrics,

/// Callback for reply messages
required Future<void> Function(SpinifyReply reply) onReply,

/// Callback for disconnect event
required Future<void> Function() onDisconnect,
}) async {
final transport = SpinifyTransportFake()
..metrics = metrics
..onReply = onReply
..onDisconnect = onDisconnect;
await transport._connect(url);
out?.call(transport);
return transport;
};

/// Spinify fake transport
class SpinifyTransportFake implements ISpinifyTransport {
Expand Down Expand Up @@ -180,26 +183,27 @@ class SpinifyTransportFake implements ISpinifyTransport {
Duration(milliseconds: _delay),
() {
if (!_isConnected) return;
_onReply?.call(reply(DateTime.now()));
_onReply?.call(reply(DateTime.now())).ignore();
},
);

/// Metrics
late SpinifyMetrics$Mutable metrics;

/// Callback for reply messages
set onReply(void Function(SpinifyReply reply) handler) => _onReply = handler;
void Function(SpinifyReply reply)? _onReply;
set onReply(Future<void> Function(SpinifyReply reply) handler) =>
_onReply = handler;
Future<void> Function(SpinifyReply reply)? _onReply;

/// Callback for disconnect event
set onDisconnect(void Function() handler) => _onDisconnect = handler;
void Function()? _onDisconnect;
set onDisconnect(Future<void> Function() handler) => _onDisconnect = handler;
Future<void> Function()? _onDisconnect;

@override
Future<void> disconnect([int? code, String? reason]) async {
if (!_isConnected) return;
await _sleep();
_onDisconnect?.call();
await _onDisconnect?.call();
_timer?.cancel();
_timer = null;
}
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,4 @@ dev_dependencies:
benchmark_harness: ^2.2.2
lints: ^3.0.0
test: ^1.24.4
fake_async: ^1.3.1
40 changes: 38 additions & 2 deletions test/unit/spinify_test.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import 'package:fake_async/fake_async.dart';
import 'package:spinify/spinify.dart';
import 'package:test/test.dart';

void main() {
group('Spinify', () {
Spinify createFakeClient() => Spinify(
Spinify createFakeClient([
void Function(ISpinifyTransport transport)? out,
]) =>
Spinify(
config: SpinifyConfig(
transportBuilder: $createFakeSpinifyTransport,
transportBuilder: $createFakeSpinifyTransport(out),
),
);

Expand Down Expand Up @@ -58,5 +62,37 @@ void main() {
isA<SpinifyState$Closed>()
]));
});

test(
'Reconnect_after_disconnected_transport',
() => fakeAsync((async) {
ISpinifyTransport? transport;
final client = createFakeClient((t) => transport = t)
..connect('ws://localhost:8000/connection/websocket');
expect(client.state, isA<SpinifyState$Connecting>());
async.elapse(client.config.timeout);
expect(client.state, isA<SpinifyState$Connected>());
expect(transport, isNotNull);
expect(transport, isA<SpinifyTransportFake>());
transport!.disconnect();
async.elapse(const Duration(milliseconds: 50));
expect(client.state, isA<SpinifyState$Disconnected>());
async.elapse(Duration(
milliseconds: client
.config.connectionRetryInterval.min.inMilliseconds ~/
2));
expect(client.state, isA<SpinifyState$Disconnected>());
async.elapse(client.config.connectionRetryInterval.max);
expect(client.state, isA<SpinifyState$Connected>());
client.close();
expectLater(
client.states,
emitsInOrder([
isA<SpinifyState$Disconnected>(),
isA<SpinifyState$Closed>()
]));
async.elapse(client.config.connectionRetryInterval.max);
expect(client.state, isA<SpinifyState$Closed>());
}));
});
}

0 comments on commit aba9baf

Please sign in to comment.