From b3cac708f7cc8d6b0c64b9ddebfb8cf7fe1b00ad Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Sun, 3 Dec 2023 17:56:00 +1300 Subject: [PATCH] Fix analysis --- analysis_options.yaml | 4 +++ bin/run_verifier.dart | 42 ++++++++++++----------- lib/bucketer.dart | 13 +++---- lib/chunker.dart | 2 +- lib/const.dart | 2 +- lib/crypto.dart | 11 +++--- lib/debug_file.dart | 23 +++++++------ lib/defender.dart | 39 +++++++++------------ lib/hasher.dart | 27 +++++++-------- lib/key_store.dart | 8 ++--- lib/main.dart | 39 +++++++++++---------- lib/metadata.dart | 5 ++- lib/microphone.dart | 17 ++++----- lib/pipeline.dart | 14 ++++---- lib/saf_code_builder.dart | 8 ++--- lib/util.dart | 37 ++++++++++++++++++-- lib/verifier.dart | 33 +++++++++--------- pubspec.lock | 48 +++++++++++++------------- pubspec.yaml | 3 ++ test/bucketer_generated_test.dart | 8 ++--- test/chunker_test.dart | 8 ++--- test/crypto_test.dart | 13 ++++--- test/hasher_test.dart | 8 ++--- test/metadata_test.dart | 9 +++-- test/pipeline_generated_test.dart | 8 ++--- test/test_util.dart | 15 ++++---- test/util_test.dart | 8 ++--- test/verifier_test.dart | 57 +++++++++++++++++-------------- 28 files changed, 269 insertions(+), 240 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 120e6fa..b94ff3d 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -12,3 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. include: package:flutter_lints/flutter.yaml + +linter: + rules: + - prefer_double_quotes diff --git a/bin/run_verifier.dart b/bin/run_verifier.dart index ad88b6c..0c8eeeb 100644 --- a/bin/run_verifier.dart +++ b/bin/run_verifier.dart @@ -12,16 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; -import 'dart:io'; -import 'dart:math'; -import 'dart:typed_data'; -import 'package:deep_defender/const.dart'; -import 'package:deep_defender/crypto.dart'; -import 'package:deep_defender/hasher.dart'; -import 'package:deep_defender/metadata.dart'; -import 'package:deep_defender/verifier.dart'; -import 'package:wav/wav.dart'; +import "dart:convert"; +import "dart:io"; +import "dart:math"; +import "dart:typed_data"; +import "package:logging/logging.dart"; +import "package:deep_defender/const.dart"; +import "package:deep_defender/crypto.dart"; +import "package:deep_defender/verifier.dart"; +import "package:wav/wav.dart"; + +final _log = Logger("run_verifier"); class TimedSafCode { int timeMs; @@ -41,7 +42,7 @@ TimedSafCode? parseSafCode(String line) { List parseSafCodes(String safCodes) { final a = []; - for (final line in safCodes.split('\n')) { + for (final line in safCodes.split("\n")) { final saf = parseSafCode(line.trim()); if (saf != null) { a.add(saf); @@ -50,24 +51,25 @@ List parseSafCodes(String safCodes) { return a; } -debugWav(Float64List audio, [String name = 'debug']) { - Wav([audio.sublist(0)], kSampleRate).writeFile('$name.wav'); +debugWav(Float64List audio, [String name = "debug"]) { + Wav([audio.sublist(0)], kSampleRate).writeFile("$name.wav"); } main(List args) async { if (args.length != 3) { - print('Wrong number of args. Usage:'); - print(' dart run run_verifier.dart input.wav safCodes.txt key.json'); + _log.severe("Wrong number of args. Usage:"); + _log.severe(" dart run run_verifier.dart input.wav safCodes.txt key.json"); return; } final wav = await Wav.readFile(args[0]); assert(wav.samplesPerSecond == kSampleRate); final audio = wav.toMono(); - print('Wav is ${(audio.length / kSampleRate).toStringAsFixed(2)} sec long'); + _log.info( + "Wav is ${(audio.length / kSampleRate).toStringAsFixed(2)} sec long"); final safCodes = parseSafCodes(await File(args[1]).readAsString()); - print('Loaded ${safCodes.length} SAF codes'); + _log.info("Loaded ${safCodes.length} SAF codes"); final firstCodeTimeSec = safCodes[0].timeMs / 1000.0; final audioStartTimeSec = max(0, firstCodeTimeSec - 2); final audioStartSample = (audioStartTimeSec * kSampleRate).toInt(); @@ -76,11 +78,11 @@ main(List args) async { final publicKey = PublicKey.fromJwk(await File(args[2]).readAsString()); final verifier = SafCodeVerifier(publicKey, (VerifierResult result) { - print("${result.error}\t${result.score}\t${result.header?.time}"); + _log.info("${result.error}\t${result.score}\t${result.header?.time}"); //final volume = Hasher.u32ToVol( // ByteData.sublistView(result.safCode).getUint32( // Metadata.length, Endian.big)); - //print("${volume},${result.score}"); + //_log.info("${volume},${result.score}"); //if (result.audio != null) { // debugWav(result.audio!.matchedAudio, 'chunk ${result.audio!.audioTime}'); //} @@ -89,5 +91,5 @@ main(List args) async { for (final tsc in safCodes) { await verifier.addSafCode(tsc.safCode); } - print('Done'); + _log.info("Done"); } diff --git a/lib/bucketer.dart b/lib/bucketer.dart index 10f4daa..8558078 100644 --- a/lib/bucketer.dart +++ b/lib/bucketer.dart @@ -12,13 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:math' as math; -import 'dart:typed_data'; +import "dart:typed_data"; -import 'package:fftea/fftea.dart'; +import "package:fftea/fftea.dart"; -import 'const.dart'; -import 'util.dart'; +import "util.dart"; // Receives a stream of timestamped audio data, in fixed sized chunks, runs // STFT, and calculates power levels of coarser buckets of frequencies. @@ -45,7 +43,6 @@ class Bucketer { _itr = logLinItr(1 + stftSize ~/ 2, buckets, grad0: 3); void onData(int timeMs, Float64List chunk) { - int j = 0; _stft.run(chunk, (Float64x2List freq) { int k = 0; final a = freq.discardConjugates(); @@ -75,8 +72,8 @@ class _STFT { _chunk = Float64x2List(chunkSize) { if (_win != null && _win!.length != chunkSize) { throw ArgumentError( - 'Window must have the same length as the chunk size.', - '_win', + "Window must have the same length as the chunk size.", + "_win", ); } } diff --git a/lib/chunker.dart b/lib/chunker.dart index 614fca9..2aea939 100644 --- a/lib/chunker.dart +++ b/lib/chunker.dart @@ -19,7 +19,7 @@ // As soon as the chunk callback is finished, the data buffer that was sent will // be overwritten, so the callback should copy any data it needs. -import 'dart:typed_data'; +import "dart:typed_data"; class Chunker { final double _sampleRateMs; diff --git a/lib/const.dart b/lib/const.dart index 1c9faa9..c2d1e1a 100644 --- a/lib/const.dart +++ b/lib/const.dart @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; +import "dart:typed_data"; const String kMagicString = "SAF"; const int kVersion = 1; diff --git a/lib/crypto.dart b/lib/crypto.dart index a3a223b..07cc13f 100644 --- a/lib/crypto.dart +++ b/lib/crypto.dart @@ -12,11 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; -import 'dart:typed_data'; +import "dart:convert"; +import "dart:typed_data"; -import 'package:crypto_keys/crypto_keys.dart' as ck; -import 'package:pointycastle/src/utils.dart' show encodeBigInt, decodeBigInt; +import "package:crypto_keys/crypto_keys.dart" as ck; + +import "util.dart"; /// Wrapper around crypto_keys to abstract away the details. For example, we /// should be able to switch the crypto algorithm without changing the rest of @@ -107,7 +108,7 @@ class KeyPair { final x = _bigIntToBase64(publicKey._key.xCoordinate); final y = _bigIntToBase64(publicKey._key.yCoordinate); final pub = '"x":"$x","y":"$y"'; - String priv = ''; + String priv = ""; if (includePrivateKey) { final d = _bigIntToBase64(privateKey._key.eccPrivateKey); priv = ',"d":"$d"'; diff --git a/lib/debug_file.dart b/lib/debug_file.dart index c29a178..9d913d1 100644 --- a/lib/debug_file.dart +++ b/lib/debug_file.dart @@ -12,12 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; -import 'package:path_provider/path_provider.dart'; -import 'package:wav/wav.dart'; -import 'const.dart'; +import "dart:convert"; +import "dart:typed_data"; +import "package:logging/logging.dart"; +import "package:path_provider/path_provider.dart"; +import "package:wav/wav.dart"; +import "const.dart"; + +final _log = Logger("debug_file"); // Saves an audio chunks as wav file, with its SAF code as the filename. This is // just for debugging. @@ -30,10 +32,11 @@ class DebugFile { ++_debugIndex; final audioCopy = audio.sublist(0); // Make sure to copy before any awaits. final dir = await directory(); - final filename = '${_debugIndex}_${base64Url.encode(safCode)}.wav'; - final path = '$dir/$filename'; - print( - "C:/Users/tiusic/AppData/Local/Android/Sdk/platform-tools/adb.exe pull $path $filename"); + final filename = "${_debugIndex}_${base64Url.encode(safCode)}.wav"; + final path = "$dir/$filename"; + _log.info( + "C:/Users/tiusic/AppData/Local/Android/Sdk/platform-tools/adb.exe pull " + "$path $filename"); await Wav([audioCopy], kSampleRate).writeFile(path); } diff --git a/lib/defender.dart b/lib/defender.dart index 5a3d90a..c78728e 100644 --- a/lib/defender.dart +++ b/lib/defender.dart @@ -12,21 +12,19 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:isolate'; -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:qr/qr.dart'; - -import 'const.dart'; -import 'crypto.dart'; -import 'debug_file.dart'; -import 'metadata.dart'; -import 'microphone.dart'; -import 'pipeline.dart'; -import 'saf_code_builder.dart'; -import 'util.dart'; +import "dart:isolate"; +import "dart:typed_data"; + +import "package:flutter/services.dart"; +import "package:qr/qr.dart"; + +import "const.dart"; +import "crypto.dart"; +import "debug_file.dart"; +import "metadata.dart"; +import "microphone.dart"; +import "pipeline.dart"; +import "saf_code_builder.dart"; /// Connects a Microphone to a SafCodeBuilder etc running in a separate Isolate. /// @@ -42,20 +40,18 @@ class Defender { final void Function(int, QrCode) _setQr; final Future _privateKey; final _recv = ReceivePort(); - late final Future _microphone; - late final Future _isolate; SendPort? _send; Defender(this._setQr, this._privateKey) { _recv.listen(_onMessage); final rootToken = RootIsolateToken.instance!; - _isolate = Isolate.spawn(defenderIsolateMain, [_recv.sendPort, rootToken]); - _microphone = Microphone.mic(_updateCode); + Isolate.spawn(defenderIsolateMain, [_recv.sendPort, rootToken]); + Microphone.mic(_updateCode); } void _onMessage(dynamic message) { if (message is SendPort) { - _send = message as SendPort; + _send = message; _privateKey.then((pk) => _send?.send(pk)); } else { final qrm = message as QrMessage; @@ -108,8 +104,7 @@ class DefenderIsolate { void _onMessage(dynamic message) { if (message is PrivateKey) { - _codeBuilder = - SafCodeBuilder(Metadata(), (message as PrivateKey).signer()); + _codeBuilder = SafCodeBuilder(Metadata(), (message).signer()); } else { final am = message as AudioMessage; _pipeline.onData(am.timeMs, am.audio); diff --git a/lib/hasher.dart b/lib/hasher.dart index aafe872..90d2490 100644 --- a/lib/hasher.dart +++ b/lib/hasher.dart @@ -12,12 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; +import "dart:typed_data"; -import 'package:fftea/fftea.dart'; - -import 'const.dart'; -import 'util.dart'; +import "util.dart"; // Receives a stream frequency buckets. Yields a stream of hashes. // @@ -33,7 +30,7 @@ class Hasher { late final Uint32List _volume; late final Uint64List _hashes; - final Float64List _prevPow_dF; + final Float64List _prevPowDF; final Function(int, Float64List, Uint8List) _reportFingerprint; static const kReset = -1; @@ -43,7 +40,7 @@ class Hasher { Hasher(int bitsPerHash, int hashesPerChunk, this._reportFingerprint) : _size = 8 * hashesPerChunk + 4, - _prevPow_dF = Float64List(bitsPerHash) { + _prevPowDF = Float64List(bitsPerHash) { // We want the _hashes array to be 8-byte aligned, even to the volume prefix // is 4 bytes. So allocate an array with an additional 4 byte prefix. final bufSize = _size + 4; @@ -54,16 +51,16 @@ class Hasher { } void onData(Float64List powers) { - assert(powers.length == _prevPow_dF.length + 1); + assert(powers.length == _prevPowDF.length + 1); int h = 0; - for (int i = 0; i < _prevPow_dF.length; ++i) { - final pow_dF = powers[i + 1] - powers[i]; + for (int i = 0; i < _prevPowDF.length; ++i) { + final powDF = powers[i + 1] - powers[i]; if (_k >= 0) { - if (pow_dF - _prevPow_dF[i] > 0) { + if (powDF - _prevPowDF[i] > 0) { h |= 1 << i; } } - _prevPow_dF[i] = pow_dF; + _prevPowDF[i] = powDF; } if (_k >= 0) { _hashes[_k] = h; @@ -78,7 +75,7 @@ class Hasher { _k = kReset; } - static const int U32MAX = (1 << 32) - 1; - static int volToU32(double volume) => (clamp(volume, 0, 1) * U32MAX).toInt(); - static double u32ToVol(int u32) => u32.toDouble() / U32MAX; + static const int u32Max = (1 << 32) - 1; + static int volToU32(double volume) => (clamp(volume, 0, 1) * u32Max).toInt(); + static double u32ToVol(int u32) => u32.toDouble() / u32Max; } diff --git a/lib/key_store.dart b/lib/key_store.dart index feeb8d7..dd8e86b 100644 --- a/lib/key_store.dart +++ b/lib/key_store.dart @@ -12,12 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; +import "package:flutter/foundation.dart"; +import "package:shared_preferences/shared_preferences.dart"; -import 'package:flutter/foundation.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import 'crypto.dart'; +import "crypto.dart"; /// Provides an RSA public/private key pair. /// diff --git a/lib/main.dart b/lib/main.dart index a690e74..3be125e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,51 +12,50 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:qr_flutter/qr_flutter.dart'; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; +import "package:share_plus/share_plus.dart"; +import "package:qr_flutter/qr_flutter.dart"; -import 'defender.dart'; -import 'microphone.dart'; -import 'key_store.dart'; +import "defender.dart"; +import "key_store.dart"; void main() { - runApp(DeepDefenderApp()); + runApp(const DeepDefenderApp()); } class DeepDefenderApp extends StatelessWidget { + const DeepDefenderApp({super.key}); + @override Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); return MaterialApp( - title: 'Deep Defender', + title: "Deep Defender", theme: ThemeData( primarySwatch: Colors.deepOrange, visualDensity: VisualDensity.adaptivePlatformDensity, ), - home: DefenderPage(title: 'Deep Defender'), + home: const DefenderPage(title: "Deep Defender"), ); } } +final KeyStore _keyStore = KeyStore(); + class DefenderPage extends StatefulWidget { final String title; - final _keyStore = KeyStore(); - DefenderPage({Key? key, required this.title}) : super(key: key); + const DefenderPage({super.key, required this.title}); @override - _DefenderState createState() => _DefenderState(_keyStore); + State createState() => _DefenderState(); } class _DefenderState extends State { - final KeyStore _keyStore; - late final Defender _defender; QrCode? _qr; String _text = "Waiting to hear from the microphone..."; - _DefenderState(this._keyStore) { - _defender = Defender(_setQr, _keyStore.privateKey()); + _DefenderState() { + Defender(_setQr, _keyStore.privateKey()); } void _setQr(int timeMs, QrCode qr) { @@ -80,7 +79,7 @@ class _DefenderState extends State { >[ PopupMenuItem( value: _sharePublicKey, - child: Text('Share public key'), + child: const Text("Share public key"), ), ], ), @@ -93,7 +92,7 @@ class _DefenderState extends State { if (_qr != null) QrImageView.withQr(qr: _qr!) else - AspectRatio(aspectRatio: 1), + const AspectRatio(aspectRatio: 1), Text( _text, textAlign: TextAlign.center, diff --git a/lib/metadata.dart b/lib/metadata.dart index a471361..2220eae 100644 --- a/lib/metadata.dart +++ b/lib/metadata.dart @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; +import "dart:typed_data"; -import 'const.dart'; -import 'util.dart'; +import "const.dart"; // Generates SAF code metadata. class Metadata { diff --git a/lib/microphone.dart b/lib/microphone.dart index 724086c..7c6a837 100644 --- a/lib/microphone.dart +++ b/lib/microphone.dart @@ -12,14 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:async'; -import 'dart:typed_data'; +import "dart:async"; -import 'package:flutter_audio_capture/flutter_audio_capture.dart'; -import 'package:permission_handler/permission_handler.dart'; +import "package:flutter_audio_capture/flutter_audio_capture.dart"; +import "package:logging/logging.dart"; +import "package:permission_handler/permission_handler.dart"; -import 'const.dart'; -import 'util.dart'; +import "const.dart"; + +final _log = Logger("microphone"); class Microphone { static Future mic(void Function(int, MicData) callback) async { @@ -36,10 +37,10 @@ class Microphone { Future _start() => _captor.start( _onData, - print, + _log.severe, sampleRate: kSampleRate, bufferSize: 2000, - firstDataTimeout: Duration(minutes: 1), + firstDataTimeout: const Duration(minutes: 1), waitForFirstDataOnAndroid: false, ); diff --git a/lib/pipeline.dart b/lib/pipeline.dart index 2cd39b9..25c4fc3 100644 --- a/lib/pipeline.dart +++ b/lib/pipeline.dart @@ -12,15 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; +import "dart:typed_data"; -import 'package:fftea/fftea.dart'; - -import 'bucketer.dart'; -import 'chunker.dart'; -import 'const.dart'; -import 'hasher.dart'; -import 'util.dart'; +import "bucketer.dart"; +import "chunker.dart"; +import "const.dart"; +import "hasher.dart"; +import "util.dart"; /// Chains together Chunker -> Bucketer -> Hasher, for use in the app, and for /// integration testing. diff --git a/lib/saf_code_builder.dart b/lib/saf_code_builder.dart index 9b6085c..cd8c209 100644 --- a/lib/saf_code_builder.dart +++ b/lib/saf_code_builder.dart @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; +import "dart:typed_data"; -import 'crypto.dart'; -import 'metadata.dart'; +import "crypto.dart"; +import "metadata.dart"; // Combines the audio fingerprint, signature, and metadata into a SAF code. class SafCodeBuilder { @@ -24,7 +24,7 @@ class SafCodeBuilder { bool init = false; late Uint8List _buf; late ByteData _bytes; - SafCodeBuilder(this._metadata, this._signer) {} + SafCodeBuilder(this._metadata, this._signer); Uint8List generate(int timeMs, Uint8List fingerprint) { // [metadata] [audio fingerprint] [signature] diff --git a/lib/util.dart b/lib/util.dart index f036048..d6fbeea 100644 --- a/lib/util.dart +++ b/lib/util.dart @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:math' as math; -import 'dart:typed_data'; +import "dart:math" as math; +import "dart:typed_data"; -import 'const.dart'; +import "const.dart"; // Returns x such that f(X) = y and abs(X - x) < dx // That is, the solution of f(x) = y, to an accuracy of dx. Assumes f is: @@ -99,3 +99,34 @@ double rmsVolume(Float64List a) { } return math.sqrt(sum / a.length); } + +// Copied from package:pointycastle/src/utils.dart. +final _byteMask = BigInt.from(0xff); +final _negativeFlag = BigInt.from(0x80); +Uint8List encodeBigInt(BigInt number) { + if (number == BigInt.zero) { + return Uint8List.fromList([0]); + } + + int needsPaddingByte; + int rawSize; + + if (number > BigInt.zero) { + rawSize = (number.bitLength + 7) >> 3; + needsPaddingByte = + ((number >> (rawSize - 1) * 8) & _negativeFlag) == _negativeFlag + ? 1 + : 0; + } else { + needsPaddingByte = 0; + rawSize = (number.bitLength + 8) >> 3; + } + + final size = rawSize + needsPaddingByte; + var result = Uint8List(size); + for (var i = 0; i < rawSize; i++) { + result[size - i - 1] = (number & _byteMask).toInt(); + number = number >> 8; + } + return result; +} diff --git a/lib/verifier.dart b/lib/verifier.dart index dde55d7..7024668 100644 --- a/lib/verifier.dart +++ b/lib/verifier.dart @@ -12,18 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:collection'; -import 'dart:math'; -import 'dart:typed_data'; -import 'package:wav/util.dart'; +import "dart:collection"; +import "dart:math"; +import "dart:typed_data"; -import 'bucketer.dart'; -import 'hasher.dart'; +import "hasher.dart"; -import 'const.dart'; -import 'crypto.dart'; -import 'metadata.dart'; -import 'pipeline.dart'; +import "const.dart"; +import "crypto.dart"; +import "metadata.dart"; +import "pipeline.dart"; const double _minAllowedSpeed = 0.95; const double _maxAllowedSpeed = 2 - _minAllowedSpeed; @@ -71,8 +69,8 @@ class SafCodeVerifier { if (_flushing) return; // TODO(#5): This is a hack. Switch to a stream API. _flushing = true; - while (_audio.length >= _minAudioCodeSize && _safCodes.length > 0) { - this._onResult(await _verify(_safCodes.removeFirst())); + while (_audio.length >= _minAudioCodeSize && _safCodes.isNotEmpty) { + _onResult(await _verify(_safCodes.removeFirst())); } _flushing = false; } @@ -221,9 +219,9 @@ class _HashCheck { static const double _minAllowedScore = 0.8; double minAllowedScore() => _minAllowedScore; - Uint8List _target; - Float64List _possibleChunk; - double _chunkStartTimeSec; + final Uint8List _target; + final Float64List _possibleChunk; + final double _chunkStartTimeSec; _HashCheck(this._target, this._possibleChunk, this._chunkStartTimeSec); @@ -342,6 +340,7 @@ class VerifierResult { VerifierResult(this.safCode, this.error, this.score, [this.header, this.audio]); + @override String toString() => error.toString(); } @@ -379,7 +378,9 @@ class _TimingEstimate { double maxAudioTimeSec; _TimingEstimate(this.minAudioTimeSec, this.estAudioTimeSec, this.targetAudioTimeSec, this.maxAudioTimeSec); - String toString() => '{$minAudioTimeSec, $estAudioTimeSec, $maxAudioTimeSec}'; + + @override + String toString() => "{$minAudioTimeSec, $estAudioTimeSec, $maxAudioTimeSec}"; } class _TimingEstimator { diff --git a/pubspec.lock b/pubspec.lock index 37e0c53..6e3e5fe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,18 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "36a321c3d2cbe01cbcb3540a87b8843846e0206df3e691fa7b23e19e78de6d49" + sha256: eb376e9acf6938204f90eb3b1f00b578640d3188b4c8a8ec054f9f479af8d051 url: "https://pub.dev" source: hosted - version: "65.0.0" + version: "64.0.0" analyzer: dependency: transitive description: name: analyzer - sha256: dfe03b90ec022450e22513b5e5ca1f01c0c01de9c3fba2f7fd233cb57a6b9a07 + sha256: "69f54f967773f6c26c7dcb13e93d7ccee8b17a641689da39e878d5cf13b06893" url: "https://pub.dev" source: hosted - version: "6.3.0" + version: "6.2.0" archive: dependency: transitive description: @@ -174,6 +174,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.9.3" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: e2a421b7e59244faef694ba7b30562e489c2b489866e505074eb005cd7060db7 + url: "https://pub.dev" + source: hosted + version: "3.0.1" flutter_test: dependency: "direct dev" description: flutter @@ -240,24 +248,16 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "04be76c4a4bb50f14904e64749237e541e7c7bcf7ec0b196907322ab5d2fc739" - url: "https://pub.dev" - source: hosted - version: "9.0.16" - leak_tracker_testing: + lints: dependency: transitive description: - name: leak_tracker_testing - sha256: b06739349ec2477e943055aea30172c5c7000225f79dad4702e2ec0eda79a6ff + name: lints + sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 url: "https://pub.dev" source: hosted - version: "1.0.5" + version: "3.0.0" logging: - dependency: transitive + dependency: "direct main" description: name: logging sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" @@ -276,18 +276,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.10.0" mime: dependency: transitive description: @@ -465,7 +465,7 @@ packages: source: hosted version: "2.1.4" qr: - dependency: transitive + dependency: "direct main" description: name: qr sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" @@ -833,10 +833,10 @@ packages: dependency: transitive description: name: web - sha256: edc8a9573dd8c5a83a183dae1af2b6fd4131377404706ca4e5420474784906fa + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "0.3.0" web_socket_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 8b73b12..d734518 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,8 +39,11 @@ dependencies: wav: ^1.3.0 pem: ^2.0.4 path_provider: ^2.1.0 + logging: ^1.2.0 + qr: ^3.0.1 dev_dependencies: + flutter_lints: ^3.0.1 flutter_test: sdk: flutter diff --git a/test/bucketer_generated_test.dart b/test/bucketer_generated_test.dart index bae249d..5fc7474 100644 --- a/test/bucketer_generated_test.dart +++ b/test/bucketer_generated_test.dart @@ -15,13 +15,11 @@ // GENERATED FILE. DO NOT EDIT. Generated with: // python3 test/generate_bucketer_test.py -import 'dart:typed_data'; -import 'package:test/test.dart'; -import 'package:deep_defender/bucketer.dart'; -import 'test_util.dart'; +import "package:test/test.dart"; +import "test_util.dart"; main() { - test('Bucketer', () { + test("Bucketer", () { testBucketer( 128, 64, diff --git a/test/chunker_test.dart b/test/chunker_test.dart index d92ba09..0b62443 100644 --- a/test/chunker_test.dart +++ b/test/chunker_test.dart @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; -import 'package:test/test.dart'; -import 'package:deep_defender/chunker.dart'; +import "dart:typed_data"; +import "package:test/test.dart"; +import "package:deep_defender/chunker.dart"; Float64List makeList(int from, int to) { final a = []; @@ -25,7 +25,7 @@ Float64List makeList(int from, int to) { } main() { - test('Chunker', () { + test("Chunker", () { // 0ms 30ms 60ms // [0 1 2 3 4 5 6 7] // [6 7 8 9 10 11 12 13] diff --git a/test/crypto_test.dart b/test/crypto_test.dart index ef3104f..10536ae 100644 --- a/test/crypto_test.dart +++ b/test/crypto_test.dart @@ -12,11 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:convert'; -import 'dart:typed_data'; -import 'package:deep_defender/crypto.dart'; -import 'package:deep_defender/key_store.dart'; -import 'package:test/test.dart'; +import "dart:convert"; +import "dart:typed_data"; +import "package:deep_defender/crypto.dart"; +import "package:test/test.dart"; const kTestKey = '{"kty":"EC","alg":"ES256","use":"sig","crv":"P-256","x":"AJXCXc4kQBQm' @@ -24,7 +23,7 @@ const kTestKey = 'OhVMlzkB-9AJc=","d":"AK86ItzaEXq05gA5LhLSnAmP6aPLrSotlSqq67TvgvIx"}'; main() { - test('Byte signer produces correct signature', () { + test("Byte signer produces correct signature", () { final keyPair = KeyPair.fromJwk(kTestKey); final signer = keyPair.privateKey.signer(); final input = utf8.encode("Hello World!"); @@ -39,7 +38,7 @@ main() { expect(keyPair.publicKey.verifier().verifyInline(bytes), isTrue); }); - test('JWK', () { + test("JWK", () { final keyPair = KeyPair.fromJwk(kTestKey); expect(keyPair.toJwk(includePrivateKey: true), kTestKey); expect( diff --git a/test/hasher_test.dart b/test/hasher_test.dart index ab3e2e6..4beea4f 100644 --- a/test/hasher_test.dart +++ b/test/hasher_test.dart @@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; -import 'package:test/test.dart'; -import 'package:deep_defender/hasher.dart'; +import "dart:typed_data"; +import "package:test/test.dart"; +import "package:deep_defender/hasher.dart"; main() { - test('Hasher', () { + test("Hasher", () { final hasher = Hasher(8, 4, (timeMs, chunk, hashes) { expect(timeMs, 1234); expect(hashes.length, 36); diff --git a/test/metadata_test.dart b/test/metadata_test.dart index 74dd25c..d20ad38 100644 --- a/test/metadata_test.dart +++ b/test/metadata_test.dart @@ -12,13 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:math'; -import 'dart:typed_data'; -import 'package:test/test.dart'; -import 'package:deep_defender/metadata.dart'; +import "dart:typed_data"; +import "package:test/test.dart"; +import "package:deep_defender/metadata.dart"; main() { - test('Metadata filler matches spec', () { + test("Metadata filler matches spec", () { final buf = Uint8List(Metadata.length); Metadata().fill(0x0123456789ABCDEF, buf.buffer.asByteData()); expect( diff --git a/test/pipeline_generated_test.dart b/test/pipeline_generated_test.dart index e18901c..da560a3 100644 --- a/test/pipeline_generated_test.dart +++ b/test/pipeline_generated_test.dart @@ -15,13 +15,11 @@ // GENERATED FILE. DO NOT EDIT. Generated with: // python3 test/generate_pipeline_test.py -import 'dart:typed_data'; -import 'package:test/test.dart'; -import 'package:deep_defender/pipeline.dart'; -import 'test_util.dart'; +import "package:test/test.dart"; +import "test_util.dart"; main() { - test('Pipeline', () async { + test("Pipeline", () async { await testPipeline( "test.wav", 28672, diff --git a/test/test_util.dart b/test/test_util.dart index 1d9eb49..d84751f 100644 --- a/test/test_util.dart +++ b/test/test_util.dart @@ -12,14 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:typed_data'; +import "dart:typed_data"; -import 'package:deep_defender/bucketer.dart'; -import 'package:deep_defender/const.dart'; -import 'package:deep_defender/pipeline.dart'; -import 'package:deep_defender/util.dart'; -import 'package:test/test.dart'; -import 'package:wav/wav.dart'; +import "package:deep_defender/bucketer.dart"; +import "package:deep_defender/const.dart"; +import "package:deep_defender/pipeline.dart"; +import "package:test/test.dart"; +import "package:wav/wav.dart"; void expectClose(List out, List exp, [double delta = 1e-6]) { expect(out.length, exp.length); @@ -62,7 +61,7 @@ Future testPipeline( int hashStride, int bitsPerHash, List> expectedHashes) async { - final wav = await Wav.readFile('test/$filename'); + final wav = await Wav.readFile("test/$filename"); final audio = wav.toMono(); final data = MicData.fromList(audio); final actualHashes = []; diff --git a/test/util_test.dart b/test/util_test.dart index f19a53e..111ec83 100644 --- a/test/util_test.dart +++ b/test/util_test.dart @@ -12,17 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'package:test/test.dart'; -import 'package:deep_defender/util.dart'; +import "package:test/test.dart"; +import "package:deep_defender/util.dart"; main() { - test('logItr', () { + test("logItr", () { expect(logItr(100, 1), [100]); expect(logItr(100, 3), [4, 21, 100]); expect(logItr(100, 10), [1, 2, 3, 6, 10, 15, 25, 39, 63, 100]); }); - test('solve', () { + test("solve", () { double cube(double x) => x * x * x; expect(solve(cube, y: 0.5, dx: 1e-3), closeTo(0.7937, 1e-3)); expect(solve(cube, y: 1000, dx: 1e-3), closeTo(10, 1e-3)); diff --git a/test/verifier_test.dart b/test/verifier_test.dart index 920c0db..f48d564 100644 --- a/test/verifier_test.dart +++ b/test/verifier_test.dart @@ -12,25 +12,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -import 'dart:async'; -import 'dart:math'; -import 'dart:typed_data'; -import 'package:deep_defender/const.dart'; -import 'package:deep_defender/crypto.dart'; -import 'package:deep_defender/metadata.dart'; -import 'package:deep_defender/pipeline.dart'; -import 'package:deep_defender/saf_code_builder.dart'; -import 'package:deep_defender/util.dart'; -import 'package:deep_defender/verifier.dart'; -import 'package:test/test.dart'; -import 'package:wav/wav.dart'; +import "dart:async"; +import "dart:math"; +import "dart:typed_data"; +import "package:deep_defender/const.dart"; +import "package:deep_defender/crypto.dart"; +import "package:deep_defender/metadata.dart"; +import "package:deep_defender/pipeline.dart"; +import "package:deep_defender/saf_code_builder.dart"; +import "package:deep_defender/util.dart"; +import "package:deep_defender/verifier.dart"; +import "package:test/test.dart"; +import "package:wav/wav.dart"; const kTestKey = '{"kty":"EC","alg":"ES256","use":"sig","crv":"P-256","x":"AJXCXc4kQBQm' 'bMLi2MpwrklFJtQC01LYCwwF-cdodv_z","y":"cyV700E19jQVyGf9S0vdsfpjj-hkhx' 'OhVMlzkB-9AJc=","d":"AK86ItzaEXq05gA5LhLSnAmP6aPLrSotlSqq67TvgvIx"}'; -Future getTestWav([String filename = 'test/test.wav']) async => +Future getTestWav([String filename = "test/test.wav"]) async => await Wav.readFile(filename); class TimedFingerprint { @@ -93,18 +93,20 @@ runTest({ final audio = (await tweakAudio?.call(rawaudio)) ?? rawaudio; //debugWav(audio); await verifier.addAudio(audio); - for (final code in allSafCodes) await verifier.addSafCode(code); + for (final code in allSafCodes) { + await verifier.addSafCode(code); + } expect(results.length, allSafCodes.length); verifyResults?.call(wav, results); } debugWav(Float64List audio) { - Wav([audio], kSampleRate).writeFile('debug.wav'); + Wav([audio], kSampleRate).writeFile("debug.wav"); } main() async { - test('Verifier ok when no change', () async { + test("Verifier ok when no change", () async { await runTest( verifyResults: (Wav wav, List results) { final audioLenMs = (wav.duration * 1000).toInt(); @@ -121,12 +123,13 @@ main() async { ); }); - test('Verifier ok when volume decreases', () async { + test("Verifier ok when volume decreases", () async { await runTest( tweakAudio: (Float64List audio) { for (int i = 0; i < audio.length; ++i) { audio[i] *= 0.1; } + return audio; }, verifyResults: (_, List results) { for (final result in results) { @@ -136,12 +139,13 @@ main() async { ); }); - test('Verifier ok when volume increases', () async { + test("Verifier ok when volume increases", () async { await runTest( tweakAudio: (Float64List audio) { for (int i = 0; i < audio.length; ++i) { audio[i] *= 10; } + return audio; }, verifyResults: (_, List results) { for (final result in results) { @@ -151,7 +155,7 @@ main() async { ); }); - test('Verifier ok when volume increases and clips', () async { + test("Verifier ok when volume increases and clips", () async { await runTest( tweakAudio: (Float64List audio) { for (int i = 0; i < audio.length; ++i) { @@ -161,6 +165,7 @@ main() async { if (audio[i] < -1) audio[i] = -1; if (audio[i] > 1) audio[i] = 1; } + return audio; }, verifyResults: (_, List results) { for (final result in results) { @@ -170,7 +175,7 @@ main() async { ); }); - test('Verifier ok when some noise', () async { + test("Verifier ok when some noise", () async { await runTest( tweakAudio: (Float64List audio) { final rand = Random(); @@ -178,6 +183,7 @@ main() async { for (int i = 0; i < audio.length; ++i) { audio[i] += (rand.nextDouble() - 0.5) * noiseAmplitude; } + return audio; }, verifyResults: (_, List results) { for (final result in results) { @@ -187,7 +193,7 @@ main() async { ); }); - test('Verifier ok when low pass filtered', () async { + test("Verifier ok when low pass filtered", () async { await runTest( tweakAudio: (Float64List audio) { double x = 0; @@ -195,6 +201,7 @@ main() async { x += 0.1 * (audio[i] - x); audio[i] = 2 * x; } + return audio; }, verifyResults: (_, List results) { for (final result in results) { @@ -204,7 +211,7 @@ main() async { ); }); - test('Verifier ok when high pass filtered', () async { + test("Verifier ok when high pass filtered", () async { await runTest( tweakAudio: (Float64List audio) { final out = Float64List(audio.length); @@ -224,7 +231,7 @@ main() async { ); }); - test('Verifier ok when audio is delayed', () async { + test("Verifier ok when audio is delayed", () async { await runTest( tweakAudio: (Float64List audio) { final offset = (kSampleRate * 0.8).toInt(); @@ -265,11 +272,11 @@ main() async { // ); //}); - test('Verifier ok when audio has been compressed', () async { + test("Verifier ok when audio has been compressed", () async { await runTest( // test_compressed.wav was created by converting to a 32kbps MP3 and back. tweakAudio: (_) async { - return (await getTestWav('test/test_compressed.wav')).toMono(); + return (await getTestWav("test/test_compressed.wav")).toMono(); }, verifyResults: (_, List results) { for (final result in results) {