Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firmware API test #113

Merged
merged 7 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ jobs:
run: flutter pub run build_runner build

- name: Test
run: flutter test --coverage
run: flutter test --coverage --exclude-tags=api

- name: Coverage clean-up
run: >
Expand Down
2 changes: 1 addition & 1 deletion firmware/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ if(ESP_PLATFORM)
set(CMAKE_CXX_STANDARD 20)

option(SHRAPNEL_RESET_WIFI_CREDENTIALS
"Remove the provisioning Wi-Fi credentials at power up"
"Remove the provisioned Wi-Fi credentials at power up"
OFF
)

Expand Down
3 changes: 3 additions & 0 deletions firmware/dist/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This folder contains firmware binaries built for distribution
*
!.gitignore
3 changes: 3 additions & 0 deletions firmware/package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
idf.py build
cp build/bootloader/bootloader.bin build/partition_table/partition-table.bin build/esp32-dsp.bin dist
3 changes: 3 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,6 @@ app.*.map.json
/android/app/release

/coverage/

# secrets used for API testing
test_config.json
2 changes: 1 addition & 1 deletion frontend/.run/All tests.run.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<configuration default="false" name="All tests" type="FlutterTestConfigType" factoryName="Flutter Test">
<option name="testDir" value="$PROJECT_DIR$/test" />
<option name="useRegexp" value="false" />
<option name="additionalArgs" value="" />
<option name="additionalArgs" value="--exclude-tags=api" />
<method v="2" />
</configuration>
</component>
3 changes: 3 additions & 0 deletions frontend/dart_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
tags:
# Firmware API tests. These tests connect to a real device running firmware.
api:
35 changes: 26 additions & 9 deletions frontend/lib/api/api_websocket.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@

import 'dart:async';

import 'package:async/async.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:logging/logging.dart';
import 'package:rxdart/rxdart.dart';

import '../audio_events.dart';
import '../core/message_transport.dart';
import '../core/stream_extensions.dart';
import '../midi_mapping/model/models.dart';
import '../parameter.dart';
Expand Down Expand Up @@ -61,29 +63,44 @@ sealed class ApiMessage with _$ApiMessage {
}) = ApiMessageSelectedPreset;
}

class ApiWebsocket {
class ApiWebsocket
implements ReconnectingMessageTransport<ApiMessage, ApiMessage> {
ApiWebsocket({
required RobustWebsocket websocket,
required ReconnectingMessageTransport<WebSocketData, WebSocketData>
websocket,
}) : _websocket = websocket;

final RobustWebsocket _websocket;
final ReconnectingMessageTransport<WebSocketData, WebSocketData> _websocket;

/// The stream of incoming messages
late final Stream<ApiMessage> stream = _websocket.dataStream
.whereType<List<int>>()
@override
late final Stream<ApiMessage> stream = _websocket.stream
.whereType<WebSocketDataBinary>()
.map((event) => event.value)
.map(shrapnel_pb.Message.fromBuffer)
.map(ApiMessageProtoEx.fromProto)
.logFinest(
_log,
(event) => 'received: $event',
);

@override
late final Stream<void> connectionStream = _websocket.connectionStream;

@override
bool get isAlive => _websocket.isAlive;

void send(ApiMessage message) {
_log.finest('sending: $message');
_websocket.sendMessage(message.toProto().writeToBuffer());
@override
late final StreamSink<ApiMessage> sink =
StreamSinkTransformer<ApiMessage, WebSocketData>.fromHandlers(
handleData: (message, sink) {
_log.finest('sending: $message');
sink.add(WebSocketData.binary(value: message.toProto().writeToBuffer()));
},
).bind(_websocket.sink);

@override
void dispose() {
_websocket.dispose();
unawaited(sink.close());
}
}
13 changes: 8 additions & 5 deletions frontend/lib/core/message_transport.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,16 @@ abstract class MessageTransport<T1, T2> {
/// Messages from the other side of the connection will appear in this stream.
Stream<T2> get stream;

/// A null is emitted every time a connection is successfully created
Stream<void> get connectionStream;
/// Must be called to clean up resource after the transport is no longer in
/// use.
void dispose();
}

abstract class ReconnectingMessageTransport<T1, T2>
extends MessageTransport<T1, T2> {
/// Returns true if the connection is alive at the moment
bool get isAlive;

/// Must be called to clean up resource after the transport is no longer in
/// use.
void dispose();
/// A null is emitted every time a connection is successfully created
Stream<void> get connectionStream;
}
2 changes: 1 addition & 1 deletion frontend/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ class App extends StatelessWidget {
RobustWebsocket? websocket,
ApiWebsocket? apiWebsocket,
WifiProvisioningService? provisioning,
MessageTransport<ParameterServiceOutputMessage,
ReconnectingMessageTransport<ParameterServiceOutputMessage,
ParameterServiceInputMessage>?
parameterTransport,
PresetsRepositoryBase? presetsRepository,
Expand Down
9 changes: 4 additions & 5 deletions frontend/lib/midi_mapping/model/service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,12 @@ import '../model/models.dart';
final _log = Logger('midi_mapping_service');

class MidiMappingTransport
implements MessageTransport<MidiApiMessage, MidiApiMessage> {
implements ReconnectingMessageTransport<MidiApiMessage, MidiApiMessage> {
MidiMappingTransport({required this.websocket}) {
_controller.stream
.logFinest(_log, (event) => 'send message: $event')
.listen(
(message) => websocket.send(ApiMessage.midiMapping(message: message)),
);
.map((message) => ApiMessage.midiMapping(message: message))
.listen(websocket.sink.add);
}

final _controller = StreamController<MidiApiMessage>();
Expand Down Expand Up @@ -96,7 +95,7 @@ class MidiMappingService extends ChangeNotifier {
/// If there is an error, the mapping is removed.
UnmodifiableMapView<MidiMappingId, MidiMapping> get mappings => _mappingsView;

MessageTransport<MidiApiMessage, MidiApiMessage> websocket;
ReconnectingMessageTransport<MidiApiMessage, MidiApiMessage> websocket;
late StreamSubscription<MidiApiMessage> _subscription;

Future<void> getMapping() async {
Expand Down
12 changes: 6 additions & 6 deletions frontend/lib/parameter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,12 @@ sealed class ParameterServiceInputMessage with _$ParameterServiceInputMessage {

class ParameterTransport
implements
MessageTransport<ParameterServiceOutputMessage,
ReconnectingMessageTransport<ParameterServiceOutputMessage,
ParameterServiceInputMessage> {
ParameterTransport({required this.websocket}) {
_controller.stream.listen((message) {
websocket.send(ApiMessage.parameterOutput(message: message));
});
_controller.stream
.map((message) => ApiMessage.parameterOutput(message: message))
.listen(websocket.sink.add);
}

ApiWebsocket websocket;
Expand Down Expand Up @@ -126,7 +126,7 @@ class ParameterTransport

class ParameterService extends ChangeNotifier {
ParameterService({
required MessageTransport<ParameterServiceOutputMessage,
required ReconnectingMessageTransport<ParameterServiceOutputMessage,
ParameterServiceInputMessage>
transport,
}) : _transport = transport {
Expand Down Expand Up @@ -162,7 +162,7 @@ class ParameterService extends ChangeNotifier {

final _parameters = <String, AudioParameterDoubleModel>{};

final MessageTransport<ParameterServiceOutputMessage,
final ReconnectingMessageTransport<ParameterServiceOutputMessage,
ParameterServiceInputMessage> _transport;

void registerParameter(AudioParameterDoubleModel parameter) {
Expand Down
12 changes: 6 additions & 6 deletions frontend/lib/presets/model/presets_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,12 @@ sealed class PresetsMessage with _$PresetsMessage {
}

class PresetsTransport
implements MessageTransport<PresetsMessage, PresetsMessage> {
implements ReconnectingMessageTransport<PresetsMessage, PresetsMessage> {
PresetsTransport({required this.websocket}) {
_controller.stream
.logFinest(_log, (event) => 'send message: $event')
.listen(
(message) => websocket.send(ApiMessage.presets(message: message)),
);
.map((message) => ApiMessage.presets(message: message))
.listen(websocket.sink.add);
}

final _controller = StreamController<PresetsMessage>();
Expand Down Expand Up @@ -158,10 +157,11 @@ class PresetsTransport

class PresetsClient {
PresetsClient({
required MessageTransport<PresetsMessage, PresetsMessage> transport,
required ReconnectingMessageTransport<PresetsMessage, PresetsMessage>
transport,
}) : _transport = transport;

final MessageTransport<PresetsMessage, PresetsMessage> _transport;
final ReconnectingMessageTransport<PresetsMessage, PresetsMessage> _transport;

/// Firmware will reply by sending a preset update message for every preset
/// that already exists.
Expand Down
17 changes: 9 additions & 8 deletions frontend/lib/presets/model/selected_preset_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ sealed class SelectedPresetMessage with _$SelectedPresetMessage {
}

class SelectedPresetTransport
implements MessageTransport<SelectedPresetMessage, SelectedPresetMessage> {
implements
ReconnectingMessageTransport<SelectedPresetMessage,
SelectedPresetMessage> {
SelectedPresetTransport({required ApiWebsocket websocket})
: _websocket = websocket {
_controller.stream
.logFinest(_log, (event) => 'send message: $event')
.listen(
(message) =>
_websocket.send(ApiMessage.selectedPreset(message: message)),
);
.map((message) => ApiMessage.selectedPreset(message: message))
.listen(_websocket.sink.add);
}

final _controller = StreamController<SelectedPresetMessage>();
Expand Down Expand Up @@ -81,12 +81,13 @@ class SelectedPresetTransport

class SelectedPresetClient {
SelectedPresetClient({
required MessageTransport<SelectedPresetMessage, SelectedPresetMessage>
required ReconnectingMessageTransport<SelectedPresetMessage,
SelectedPresetMessage>
transport,
}) : _transport = transport;

final MessageTransport<SelectedPresetMessage, SelectedPresetMessage>
_transport;
final ReconnectingMessageTransport<SelectedPresetMessage,
SelectedPresetMessage> _transport;

Future<void> initialise() async {
_transport.sink.add(SelectedPresetMessage.read());
Expand Down
Loading
Loading