diff --git a/packages/firebase_performance/example/test_driver/firebase_performance.dart b/packages/firebase_performance/example/test_driver/firebase_performance.dart index fcabfe11f239..352db529bd1b 100644 --- a/packages/firebase_performance/example/test_driver/firebase_performance.dart +++ b/packages/firebase_performance/example/test_driver/firebase_performance.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:firebase_performance/firebase_performance.dart'; void main() { final Completer completer = Completer(); @@ -10,6 +9,8 @@ void main() { tearDownAll(() => completer.complete(null)); group('firebase_performance test driver', () { + // TODO(bparrishMines): Rewrite integration tests when iOS portion is written. + /* final FirebasePerformance performance = FirebasePerformance.instance; setUp(() async { @@ -37,5 +38,6 @@ void main() { expect(trace.getAttribute('testAttribute2'), null); expect(trace.getAttribute('testMetric'), null); }); + */ }); } diff --git a/packages/firebase_performance/lib/firebase_performance.dart b/packages/firebase_performance/lib/firebase_performance.dart index 17dbcfc07863..8f8a2e7a4348 100644 --- a/packages/firebase_performance/lib/firebase_performance.dart +++ b/packages/firebase_performance/lib/firebase_performance.dart @@ -5,7 +5,6 @@ library firebase_performance; import 'dart:async'; -import 'dart:collection'; import 'package:flutter/services.dart'; import 'package:flutter/foundation.dart'; diff --git a/packages/firebase_performance/lib/src/firebase_performance.dart b/packages/firebase_performance/lib/src/firebase_performance.dart index 381236d44dc7..e2498ec172cb 100644 --- a/packages/firebase_performance/lib/src/firebase_performance.dart +++ b/packages/firebase_performance/lib/src/firebase_performance.dart @@ -11,59 +11,82 @@ enum HttpMethod { Connect, Delete, Get, Head, Options, Patch, Post, Put, Trace } /// /// You can get an instance by calling [FirebasePerformance.instance]. class FirebasePerformance { - FirebasePerformance._(); + FirebasePerformance._(this._handle) { + channel.invokeMethod( + 'FirebasePerformance#instance', + {'handle': _handle}, + ); + } + + static int _nextHandle = 0; - static int _traceCount = 0; - static int _httpMetricCount = 0; + final int _handle; @visibleForTesting static const MethodChannel channel = MethodChannel('plugins.flutter.io/firebase_performance'); /// Singleton of [FirebasePerformance]. - static final FirebasePerformance instance = FirebasePerformance._(); + static final FirebasePerformance instance = + FirebasePerformance._(_nextHandle++); /// Determines whether performance monitoring is enabled or disabled. /// /// True if performance monitoring is enabled and false if performance /// monitoring is disabled. This is for dynamic enable/disable state. This /// does not reflect whether instrumentation is enabled/disabled. - Future isPerformanceCollectionEnabled() async { - final bool isEnabled = await channel - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - .invokeMethod('FirebasePerformance#isPerformanceCollectionEnabled'); - return isEnabled; + Future isPerformanceCollectionEnabled() { + return channel.invokeMethod( + '$FirebasePerformance#isPerformanceCollectionEnabled', + {'handle': _handle}, + ); } /// Enables or disables performance monitoring. /// /// This setting is persisted and applied on future invocations of your /// application. By default, performance monitoring is enabled. - Future setPerformanceCollectionEnabled(bool enable) async { - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - await channel.invokeMethod( - 'FirebasePerformance#setPerformanceCollectionEnabled', enable); + Future setPerformanceCollectionEnabled(bool enable) { + return channel.invokeMethod( + '$FirebasePerformance#setPerformanceCollectionEnabled', + {'handle': _handle, 'enable': enable}, + ); } /// Creates a [Trace] object with given [name]. /// /// The [name] requires no leading or trailing whitespace, no leading - /// underscore _ character, max length of [Trace.maxTraceNameLength] + /// underscore _ character, and max length of [Trace.maxTraceNameLength] /// characters. Trace newTrace(String name) { - return Trace._(_traceCount++, name); + final int handle = _nextHandle++; + + FirebasePerformance.channel.invokeMethod( + '$FirebasePerformance#newTrace', + {'handle': _handle, 'traceHandle': handle, 'name': name}, + ); + + return Trace._(handle, name); } /// Creates [HttpMetric] for collecting performance for one request/response. HttpMetric newHttpMetric(String url, HttpMethod httpMethod) { - return HttpMetric._(_httpMetricCount++, url, httpMethod); + final int handle = _nextHandle++; + + FirebasePerformance.channel.invokeMethod( + '$FirebasePerformance#newHttpMetric', + { + 'handle': _handle, + 'httpMetricHandle': handle, + 'url': url, + 'httpMethod': httpMethod.toString(), + }, + ); + + return HttpMetric._(handle, url, httpMethod); } - /// Creates a [Trace] object with given [name] and start the trace. + /// Creates a [Trace] object with given [name] and starts the trace. /// /// The [name] requires no leading or trailing whitespace, no leading /// underscore _ character, max length of [Trace.maxTraceNameLength] diff --git a/packages/firebase_performance/lib/src/http_metric.dart b/packages/firebase_performance/lib/src/http_metric.dart index a2e221b368e1..c0b27bb41ce3 100644 --- a/packages/firebase_performance/lib/src/http_metric.dart +++ b/packages/firebase_performance/lib/src/http_metric.dart @@ -6,106 +6,149 @@ part of firebase_performance; /// Metric used to collect data for network requests/responses. /// -/// It is possible to have more than one httpmetric running at a time. +/// It is possible to have more than one [HttpMetric] running at a time. /// Attributes can also be added to help measure performance related events. A -/// httpmetric also measures the time between calling start() and stop(). +/// [HttpMetric] also measures the time between calling `start()` and `stop()`. /// /// Data collected is automatically sent to the associated Firebase console /// after stop() is called. /// /// You can confirm that Performance Monitoring results appear in the Firebase /// console. Results should appear within 12 hours. +/// +/// It is highly recommended that one always calls `start()` and `stop()` on +/// each created [HttpMetric] to avoid leaking on the platform side. class HttpMetric extends PerformanceAttributes { - HttpMetric._(this._handle, this._url, this._httpMethod); + HttpMetric._(this._handle, this.url, this.httpMethod); - final int _handle; - final String _url; - final HttpMethod _httpMethod; + final String url; + final HttpMethod httpMethod; + @override bool _hasStarted = false; + + @override bool _hasStopped = false; + int _httpResponseCode; + int _requestPayloadSize; + String _responseContentType; + int _responsePayloadSize; + + @override + final int _handle; + /// HttpResponse code of the request. - int httpResponseCode; + int get httpResponseCode => _httpResponseCode; /// Size of the request payload. - int requestPayloadSize; + int get requestPayloadSize => _requestPayloadSize; /// Content type of the response such as text/html, application/json, etc... - String responseContentType; + String get responseContentType => _responseContentType; /// Size of the response payload. - int responsePayloadSize; + int get responsePayloadSize => _responsePayloadSize; - /// Starts this httpmetric. + /// HttpResponse code of the request. /// - /// Can only be called once, otherwise assertion error is thrown. + /// If the [HttpMetric] has already been stopped, returns immediately without + /// taking action. + set httpResponseCode(int httpResponseCode) { + if (_hasStopped) return; + + _httpResponseCode = httpResponseCode; + FirebasePerformance.channel.invokeMethod( + '$HttpMetric#httpResponseCode', + { + 'handle': _handle, + 'httpResponseCode': httpResponseCode, + }, + ); + } + + /// Size of the request payload. /// - /// Using ```await``` with this method is only necessary when accurate timing - /// is relevant. - Future start() { - assert(!_hasStarted); + /// If the [HttpMetric] has already been stopped, returns immediately without + /// taking action. + set requestPayloadSize(int requestPayloadSize) { + if (_hasStopped) return; - _hasStarted = true; - return FirebasePerformance.channel - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - .invokeMethod('HttpMetric#start', { - 'handle': _handle, - 'url': _url, - 'httpMethod': _httpMethod.index, - }); + _requestPayloadSize = requestPayloadSize; + FirebasePerformance.channel.invokeMethod( + '$HttpMetric#requestPayloadSize', + { + 'handle': _handle, + 'requestPayloadSize': requestPayloadSize, + }, + ); } - /// Stops this httpMetric. + /// Content type of the response such as text/html, application/json, etc... /// - /// Can only be called once and only after start(), otherwise assertion error - /// is thrown. Data collected is automatically sent to the associated - /// Firebase console after stop() is called. + /// If the [HttpMetric] has already been stopped, returns immediately without + /// taking action. + set responseContentType(String responseContentType) { + if (_hasStopped) return; + + _responseContentType = responseContentType; + FirebasePerformance.channel.invokeMethod( + '$HttpMetric#responseContentType', + { + 'handle': _handle, + 'responseContentType': responseContentType, + }, + ); + } + + /// Size of the response payload. /// - /// Not necessary to use ```await``` with this method. - Future stop() { - assert(!_hasStopped); - assert(_hasStarted); - - final Map data = { - 'handle': _handle, - 'httpResponseCode': httpResponseCode, - 'requestPayloadSize': requestPayloadSize, - 'responseContentType': responseContentType, - 'responsePayloadSize': responsePayloadSize, - 'attributes': _attributes, - }; + /// If the [HttpMetric] has already been stopped, returns immediately without + /// taking action. + set responsePayloadSize(int responsePayloadSize) { + if (_hasStopped) return; - _hasStopped = true; - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - return FirebasePerformance.channel.invokeMethod('HttpMetric#stop', data); + _responsePayloadSize = responsePayloadSize; + FirebasePerformance.channel.invokeMethod( + '$HttpMetric#responsePayloadSize', + { + 'handle': _handle, + 'responsePayloadSize': responsePayloadSize, + }, + ); } - /// Sets a String [value] for the specified [attribute]. + /// Starts this [HttpMetric]. /// - /// If the httpmetric has been stopped, this method throws an assertion - /// error. + /// Can only be called once. /// - /// See [PerformanceAttributes.putAttribute]. - @override - void putAttribute(String attribute, String value) { - assert(!_hasStopped); - super.putAttribute(attribute, value); + /// Using `await` with this method is only necessary when accurate timing + /// is relevant. + Future start() { + if (_hasStopped) return Future.value(null); + + _hasStarted = true; + return FirebasePerformance.channel.invokeMethod( + '$HttpMetric#start', + {'handle': _handle}, + ); } - /// Removes an already added [attribute]. + /// Stops this [HttpMetric]. /// - /// If the httpmetric has been stopped, this method throws an assertion - /// error. + /// Can only be called once and only after start(), Data collected is + /// automatically sent to the associate Firebase console after stop() is + /// called. You can confirm that Performance Monitoring results appear in the + /// Firebase console. Results should appear within 12 hours. /// - /// See [PerformanceAttributes.removeAttribute]. - @override - void removeAttribute(String attribute) { - assert(!_hasStopped); - super.removeAttribute(attribute); + /// Not necessary to use `await` with this method. + Future stop() { + if (_hasStopped) return Future.value(null); + + _hasStopped = true; + return FirebasePerformance.channel.invokeMethod( + '$HttpMetric#stop', + {'handle': _handle}, + ); } } diff --git a/packages/firebase_performance/lib/src/performance_attributes.dart b/packages/firebase_performance/lib/src/performance_attributes.dart index 0543242fdeb2..119567a1eaf0 100644 --- a/packages/firebase_performance/lib/src/performance_attributes.dart +++ b/packages/firebase_performance/lib/src/performance_attributes.dart @@ -4,10 +4,7 @@ part of firebase_performance; -/// Abstract class that allows adding/removing attributes to any object. -/// -/// Enforces constraints for adding attributes and values required by -/// FirebasePerformance API. See [putAttribute]. +/// Abstract class that allows adding/removing attributes to an object. abstract class PerformanceAttributes { /// Maximum allowed length of a key passed to [putAttribute]. static const int maxAttributeKeyLength = 40; @@ -20,10 +17,12 @@ abstract class PerformanceAttributes { final Map _attributes = {}; - /// Copy of all the attributes added. - Map get attributes => Map.from(_attributes); + bool get _hasStarted; + bool get _hasStopped; - /// Sets a String [value] for the specified [attribute]. + int get _handle; + + /// Sets a String [value] for the specified attribute with [name]. /// /// Updates the value of the attribute if the attribute already exists. /// The maximum number of attributes that can be added are @@ -32,22 +31,48 @@ abstract class PerformanceAttributes { /// Name of the attribute has max length of [maxAttributeKeyLength] /// characters. Value of the attribute has max length of /// [maxAttributeValueLength] characters. - void putAttribute(String attribute, String value) { - assert(attribute != null); - assert(!attribute.startsWith(RegExp(r'[_\s]'))); - assert(!attribute.contains(RegExp(r'[_\s]$'))); - assert(attribute.length <= maxAttributeKeyLength); - assert(value.length <= maxAttributeValueLength); - assert(_attributes.length < maxCustomAttributes); - - _attributes[attribute] = value; + Future putAttribute(String name, String value) { + if (!_hasStarted || + _hasStopped || + name.length > maxAttributeKeyLength || + value.length > maxAttributeValueLength || + _attributes.length == 5) { + return Future.value(null); + } + + _attributes[name] = value; + return FirebasePerformance.channel.invokeMethod( + '$PerformanceAttributes#putAttribute', + { + 'handle': _handle, + 'name': name, + 'value': value, + }, + ); } - /// Removes an already added [attribute]. - void removeAttribute(String attribute) { - _attributes.remove(attribute); + /// Removes an already added attribute with [name]. + Future removeAttribute(String name) { + if (!_hasStarted || _hasStopped) return Future.value(null); + + _attributes.remove(name); + return FirebasePerformance.channel.invokeMethod( + '$PerformanceAttributes#removeAttribute', + {'handle': _handle, 'name': name}, + ); } - /// Returns the value of an [attribute]. - String getAttribute(String attribute) => _attributes[attribute]; + /// All attributes added. + Future> getAttributes() { + if (_hasStopped) { + return Future>.value(Map.unmodifiable( + _attributes, + )); + } + + return FirebasePerformance.channel.invokeMapMethod( + '$PerformanceAttributes#getAttributes', + {'handle': _handle}, + ); + } } diff --git a/packages/firebase_performance/lib/src/trace.dart b/packages/firebase_performance/lib/src/trace.dart index 96d173bef4aa..cf8e1fb01d3a 100644 --- a/packages/firebase_performance/lib/src/trace.dart +++ b/packages/firebase_performance/lib/src/trace.dart @@ -4,139 +4,120 @@ part of firebase_performance; -/// Trace allows you to set the beginning and end of a custom trace in your app. +/// [Trace] allows you to set the beginning and end of a custom trace in your app. /// /// A trace is a report of performance data associated with some of the /// code in your app. You can have multiple custom traces, and it is /// possible to have more than one custom trace running at a time. Each custom /// trace can have multiple metrics and attributes added to help measure /// performance related events. A trace also measures the time between calling -/// start() and stop(). +/// `start()` and `stop()`. /// /// Data collected is automatically sent to the associated Firebase console /// after stop() is called. /// /// You can confirm that Performance Monitoring results appear in the Firebase /// console. Results should appear within 12 hours. +/// +/// It is highly recommended that one always calls `start()` and `stop()` on +/// each created [Trace] to not avoid leaking on the platform side. class Trace extends PerformanceAttributes { - Trace._(this._handle, this._name) { - assert(_name != null); - assert(!_name.startsWith(RegExp(r'[_\s]'))); - assert(!_name.contains(RegExp(r'[_\s]$'))); - assert(_name.length <= maxTraceNameLength); - } + Trace._(this._handle, this.name); /// Maximum allowed length of the name of a [Trace]. static const int maxTraceNameLength = 100; - final int _handle; - final String _name; + final Map _metrics = {}; + @override bool _hasStarted = false; + + @override bool _hasStopped = false; - final HashMap _metrics = HashMap(); + @override + final int _handle; - /// Starts this trace. + /// Name representing this [Trace] on the Firebase Console. + final String name; + + /// Starts this [Trace]. /// - /// Can only be called once, otherwise assertion error is thrown. + /// Can only be called once. /// - /// Using ```await``` with this method is only necessary when accurate timing + /// Using `await` with this method is only necessary when accurate timing /// is relevant. Future start() { - assert(!_hasStarted); + if (_hasStopped) return Future.value(null); _hasStarted = true; - return FirebasePerformance.channel - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - .invokeMethod('Trace#start', { - 'handle': _handle, - 'name': _name, - }); + return FirebasePerformance.channel.invokeMethod( + '$Trace#start', + {'handle': _handle}, + ); } - /// Stops this trace. + /// Stops this [Trace]. /// - /// Can only be called once and only after start(), otherwise assertion error - /// is thrown. Data collected is automatically sent to the associated Firebase - /// console after stop() is called. + /// Can only be called once and only after start() Data collected is + /// automatically sent to the associated Firebase console after stop() is + /// called. You can confirm that Performance Monitoring results appear in the + /// Firebase console. Results should appear within 12 hours. /// - /// Not necessary to use ```await``` with this method. + /// Not necessary to use `await` with this method. Future stop() { - assert(!_hasStopped); - assert(_hasStarted); - - final Map data = { - 'handle': _handle, - 'name': _name, - 'metrics': _metrics, - 'attributes': _attributes, - }; + if (_hasStopped) return Future.value(null); _hasStopped = true; - // TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter. - // https://github.com/flutter/flutter/issues/26431 - // ignore: strong_mode_implicit_dynamic_method - return FirebasePerformance.channel.invokeMethod('Trace#stop', data); + return FirebasePerformance.channel.invokeMethod( + '$Trace#stop', + {'handle': _handle}, + ); } - /// Increments the counter with the given [name] by [incrementBy]. + /// Increments the metric with the given [name]. /// - /// The counter is incremented by 1 if [incrementBy] was not passed. If a - /// counter does not already exist, a new one will be created. If the trace - /// has not been started or has already been stopped, an assertion error is - /// thrown. - /// - /// The name of the counter requires no leading or - /// trailing whitespace, no leading underscore _ character, and max length of - /// 32 characters. - @Deprecated('Use `incrementMetric` instead.') - void incrementCounter(String name, [int incrementBy = 1]) { - incrementMetric(name, incrementBy); - } - - /// Increments the metric with the given [name] by [incrementBy]. - /// - /// If a metric does not already exist, a new one will be created with the - /// initial value [incrementBy]. If the trace has not been started or has - /// already been stopped, an assertion error is thrown. - /// - /// The name of the metric requires no leading or trailing whitespace, no - /// leading underscore _ character, and max length of 32 characters. - void incrementMetric(String name, int incrementBy) { - assert(!_hasStopped); - assert(name != null); - assert(!name.startsWith(RegExp(r'[_\s]'))); - assert(!name.contains(RegExp(r'[_\s]$'))); - assert(name.length <= 32); + /// If the metric does not exist, a new one will be created. If the [Trace] has + /// not been started or has already been stopped, returns immediately without + /// taking action. + Future incrementMetric(String name, int value) { + if (!_hasStarted || _hasStopped) { + return Future.value(null); + } _metrics.putIfAbsent(name, () => 0); - _metrics[name] += incrementBy; + _metrics[name] += value; + return FirebasePerformance.channel.invokeMethod( + '$Trace#incrementMetric', + {'handle': _handle, 'name': name, 'value': value}, + ); } - /// Sets a String [value] for the specified [attribute]. + /// Sets the [value] of the metric with the given [name]. /// - /// If the trace has been stopped, this method throws an assertion - /// error. - /// - /// See [PerformanceAttributes.putAttribute]. - @override - void putAttribute(String attribute, String value) { - assert(!_hasStopped); - super.putAttribute(attribute, value); + /// If a metric with the given name doesn't exist, a new one will be created. + /// If the [Trace] has not been started or has already been stopped, returns + /// immediately without taking action. + Future setMetric(String name, int value) { + if (!_hasStarted || _hasStopped) return Future.value(null); + + _metrics[name] = value; + return FirebasePerformance.channel.invokeMethod( + '$Trace#setMetric', + {'handle': _handle, 'name': name, 'value': value}, + ); } - /// Removes an already added [attribute]. + /// Gets the value of the metric with the given [name]. /// - /// If the trace has been stopped, this method throws an assertion - /// error. - /// - /// See [PerformanceAttributes.removeAttribute]. - @override - void removeAttribute(String attribute) { - assert(!_hasStopped); - super.removeAttribute(attribute); + /// If a metric with the given name doesn't exist, it is NOT created and a 0 + /// is returned. + Future getMetric(String name) { + if (_hasStopped) return Future.value(_metrics[name] ?? 0); + + return FirebasePerformance.channel.invokeMethod( + '$Trace#getMetric', + {'handle': _handle, 'name': name}, + ); } } diff --git a/packages/firebase_performance/test/firebase_performance_test.dart b/packages/firebase_performance/test/firebase_performance_test.dart index bf2b42e50499..c2dbd1997c0a 100644 --- a/packages/firebase_performance/test/firebase_performance_test.dart +++ b/packages/firebase_performance/test/firebase_performance_test.dart @@ -7,15 +7,14 @@ import 'package:flutter/services.dart'; import 'package:firebase_performance/firebase_performance.dart'; import 'package:flutter_test/flutter_test.dart'; -class MockPerformanceAttributes extends PerformanceAttributes {} - void main() { group('$FirebasePerformance', () { - final FirebasePerformance performance = FirebasePerformance.instance; + FirebasePerformance performance; final List log = []; - bool performanceCollectionEnable = true; - int currentTraceHandle; - int currentHttpMetricHandle; + bool isPerformanceCollectionEnabledResult; + + int firebasePerformanceHandle; + int nextHandle = 0; setUp(() { FirebasePerformance.channel @@ -23,20 +22,7 @@ void main() { log.add(methodCall); switch (methodCall.method) { case 'FirebasePerformance#isPerformanceCollectionEnabled': - return performanceCollectionEnable; - case 'FirebasePerformance#setPerformanceCollectionEnabled': - performanceCollectionEnable = methodCall.arguments; - return null; - case 'Trace#start': - currentTraceHandle = methodCall.arguments['handle']; - return null; - case 'Trace#stop': - return null; - case 'HttpMetric#start': - currentHttpMetricHandle = methodCall.arguments['handle']; - return null; - case 'HttpMetric#stop': - return null; + return isPerformanceCollectionEnabledResult; default: return null; } @@ -44,221 +30,163 @@ void main() { log.clear(); }); + test('instance', () { + firebasePerformanceHandle = nextHandle++; + + performance = FirebasePerformance.instance; + + expect(log, [ + isMethodCall( + 'FirebasePerformance#instance', + arguments: {'handle': firebasePerformanceHandle}, + ), + ]); + }); + test('isPerformanceCollectionEnabled', () async { + isPerformanceCollectionEnabledResult = true; final bool enabled = await performance.isPerformanceCollectionEnabled(); + expect(enabled, isTrue); + + isPerformanceCollectionEnabledResult = false; + final bool disabled = await performance.isPerformanceCollectionEnabled(); + expect(disabled, isFalse); - expect(performanceCollectionEnable, enabled); expect(log, [ isMethodCall( 'FirebasePerformance#isPerformanceCollectionEnabled', - arguments: null, + arguments: {'handle': firebasePerformanceHandle}, + ), + isMethodCall( + 'FirebasePerformance#isPerformanceCollectionEnabled', + arguments: {'handle': firebasePerformanceHandle}, ), ]); }); - test('setPerformanceCollectionEnabled', () async { - await performance.setPerformanceCollectionEnabled(true); - performanceCollectionEnable = true; - - await performance.setPerformanceCollectionEnabled(false); - performanceCollectionEnable = false; + test('setPerformanceCollectionEnabled', () { + performance.setPerformanceCollectionEnabled(true); + performance.setPerformanceCollectionEnabled(false); expect(log, [ isMethodCall( 'FirebasePerformance#setPerformanceCollectionEnabled', - arguments: true, + arguments: { + 'handle': firebasePerformanceHandle, + 'enable': true, + }, ), isMethodCall( 'FirebasePerformance#setPerformanceCollectionEnabled', - arguments: false, + arguments: { + 'handle': firebasePerformanceHandle, + 'enable': false, + }, ), ]); }); - test('newTrace', () async { - final Trace trace = performance.newTrace('test-trace'); - await trace.start(); + test('newTrace', () { + performance.newTrace('test-trace'); expect(log, [ isMethodCall( - 'Trace#start', - arguments: { - 'handle': currentTraceHandle, + 'FirebasePerformance#newTrace', + arguments: { + 'handle': firebasePerformanceHandle, + 'traceHandle': nextHandle++, 'name': 'test-trace', }, ), ]); }); - test('newHttpMetric', () async { - final HttpMetric metric = performance.newHttpMetric( - 'https://google.com', - HttpMethod.Connect, - ); - await metric.start(); + test('newHttpMetric', () { + final String url = 'https://google.com'; + performance.newHttpMetric(url, HttpMethod.Connect); expect(log, [ isMethodCall( - 'HttpMetric#start', - arguments: { - 'handle': currentTraceHandle, - 'url': 'https://google.com', - 'httpMethod': HttpMethod.Connect.index, + 'FirebasePerformance#newHttpMetric', + arguments: { + 'handle': firebasePerformanceHandle, + 'httpMetricHandle': nextHandle++, + 'url': url, + 'httpMethod': HttpMethod.Connect.toString(), }, ), ]); }); - test('startTrace', () async { - await FirebasePerformance.startTrace('startTrace-test'); + test('$HttpMethod', () { + final String url = 'https://google.com'; + final HttpMethod method = HttpMethod.Connect; + + performance.newHttpMetric('https://google.com', method); expect(log, [ isMethodCall( - 'Trace#start', - arguments: { - 'handle': currentTraceHandle, - 'name': 'startTrace-test', + 'FirebasePerformance#newHttpMetric', + arguments: { + 'handle': firebasePerformanceHandle, + 'httpMetricHandle': nextHandle++, + 'url': url, + 'httpMethod': method.toString(), }, ), ]); }); - test('$HttpMethod', () async { - expect(HttpMethod.Connect.index, 0); - expect(HttpMethod.Delete.index, 1); - expect(HttpMethod.Get.index, 2); - expect(HttpMethod.Head.index, 3); - expect(HttpMethod.Options.index, 4); - expect(HttpMethod.Patch.index, 5); - expect(HttpMethod.Post.index, 6); - expect(HttpMethod.Put.index, 7); - expect(HttpMethod.Trace.index, 8); - }); - group('$Trace', () { Trace testTrace; + int currentTestTraceHandle; setUp(() { testTrace = performance.newTrace('test'); + currentTestTraceHandle = nextHandle++; + log.clear(); }); - test('start', () async { - await testTrace.start(); + test('start', () { + testTrace.start(); expect(log, [ isMethodCall( 'Trace#start', - arguments: { - 'handle': currentTraceHandle, - 'name': 'test', - }, + arguments: {'handle': currentTestTraceHandle}, ), ]); }); - test('stop', () async { - await testTrace.start(); - await testTrace.stop(); + test('stop', () { + testTrace.start(); + log.clear(); - expect(log, [ - isMethodCall('Trace#start', arguments: { - 'handle': currentTraceHandle, - 'name': 'test', - }), - isMethodCall( - 'Trace#stop', - arguments: { - 'handle': currentTraceHandle, - 'name': 'test', - 'metrics': {}, - 'attributes': {}, - }, - ), - ]); - }); - - test('incrementCounter', () async { - final Trace trace = performance.newTrace('test'); - - // ignore: deprecated_member_use_from_same_package - trace.incrementCounter('counter1'); - - // ignore: deprecated_member_use_from_same_package - trace.incrementCounter('counter2'); - // ignore: deprecated_member_use_from_same_package - trace.incrementCounter('counter2'); - - // ignore: deprecated_member_use_from_same_package - trace.incrementCounter('counter3', 5); - // ignore: deprecated_member_use_from_same_package - trace.incrementCounter('counter3', 5); - - // ignore: deprecated_member_use_from_same_package - trace.incrementCounter('counter4', -5); - - await trace.start(); - await trace.stop(); + testTrace.stop(); expect(log, [ - isMethodCall( - 'Trace#start', - arguments: { - 'handle': currentTraceHandle, - 'name': 'test', - }, - ), isMethodCall( 'Trace#stop', - arguments: { - 'handle': currentTraceHandle, - 'name': 'test', - 'metrics': { - 'counter1': 1, - 'counter2': 2, - 'counter3': 10, - 'counter4': -5, - }, - 'attributes': {}, - }, + arguments: {'handle': currentTestTraceHandle}, ), ]); }); - test('incrementMetric', () async { - final Trace trace = performance.newTrace('test'); - trace.incrementMetric('metric1', 1); - - trace.incrementMetric('metric2', 1); - trace.incrementMetric('metric2', 1); - - trace.incrementMetric('metric3', 5); - trace.incrementMetric('metric3', 5); - - trace.incrementMetric('metric4', -5); + test('incrementMetric', () { + testTrace.start(); + log.clear(); - await trace.start(); - await trace.stop(); + final String name = 'metric1'; + final int increment = 3; + testTrace.incrementMetric(name, increment); expect(log, [ isMethodCall( - 'Trace#start', - arguments: { - 'handle': currentTraceHandle, - 'name': 'test', - }, - ), - isMethodCall( - 'Trace#stop', + 'Trace#incrementMetric', arguments: { - 'handle': currentTraceHandle, - 'name': 'test', - 'metrics': { - 'metric1': 1, - 'metric2': 2, - 'metric3': 10, - 'metric4': -5, - }, - 'attributes': {}, + 'handle': currentTestTraceHandle, + 'name': name, + 'value': increment, }, ), ]); @@ -267,105 +195,114 @@ void main() { group('$HttpMetric', () { HttpMetric testMetric; + int currentTestMetricHandle; setUp(() { testMetric = performance.newHttpMetric( 'https://google.com', HttpMethod.Get, ); + currentTestMetricHandle = nextHandle++; + log.clear(); }); - test('start', () async { - await testMetric.start(); + test('start', () { + testMetric.start(); expect(log, [ isMethodCall( 'HttpMetric#start', - arguments: { - 'handle': currentHttpMetricHandle, - 'url': 'https://google.com', - 'httpMethod': HttpMethod.Get.index, - }, + arguments: {'handle': currentTestMetricHandle}, ), ]); }); - test('stop', () async { - testMetric.httpResponseCode = 1; - testMetric.requestPayloadSize = 5000000; - testMetric.responseContentType = 'text/html'; - testMetric.responsePayloadSize = 1992304820934820; + test('stop', () { + testMetric.start(); + log.clear(); - await testMetric.start(); - await testMetric.stop(); + testMetric.stop(); expect(log, [ - isMethodCall( - 'HttpMetric#start', - arguments: { - 'handle': currentHttpMetricHandle, - 'url': 'https://google.com', - 'httpMethod': HttpMethod.Get.index, - }, - ), isMethodCall( 'HttpMetric#stop', - arguments: { - 'handle': currentHttpMetricHandle, - 'httpResponseCode': 1, - 'requestPayloadSize': 5000000, - 'responseContentType': 'text/html', - 'responsePayloadSize': 1992304820934820, - 'attributes': {}, - }, + arguments: {'handle': currentTestMetricHandle}, ), ]); }); }); group('$PerformanceAttributes', () { - PerformanceAttributes attributes; + Trace attributeTrace; + int currentTraceHandle; + + final Map getAttributesResult = { + 'a1': 'hello', + 'a2': 'friend', + }; setUp(() { - attributes = MockPerformanceAttributes(); + FirebasePerformance.channel + .setMockMethodCallHandler((MethodCall methodCall) async { + log.add(methodCall); + switch (methodCall.method) { + case 'PerformanceAttributes#getAttributes': + return getAttributesResult; + default: + return null; + } + }); + + attributeTrace = performance.newTrace('trace'); + currentTraceHandle = nextHandle++; + attributeTrace.start(); + log.clear(); }); - test('putAttribute', () async { - attributes.putAttribute('attr1', 'apple'); - attributes.putAttribute('attr2', 'are'); - expect(attributes.attributes, { - 'attr1': 'apple', - 'attr2': 'are', - }); + test('putAttribute', () { + final String name = 'attr1'; + final String value = 'apple'; - attributes.putAttribute('attr1', 'delicious'); - expect(attributes.attributes, { - 'attr1': 'delicious', - 'attr2': 'are', - }); + attributeTrace.putAttribute(name, value); + + expect(log, [ + isMethodCall( + 'PerformanceAttributes#putAttribute', + arguments: { + 'handle': currentTraceHandle, + 'name': name, + 'value': value, + }, + ), + ]); }); - test('removeAttribute', () async { - attributes.putAttribute('attr1', 'apple'); - attributes.putAttribute('attr2', 'are'); - attributes.removeAttribute('no-attr'); - expect(attributes.attributes, { - 'attr1': 'apple', - 'attr2': 'are', - }); + test('removeAttribute', () { + final String name = 'attr1'; + attributeTrace.removeAttribute(name); - attributes.removeAttribute('attr1'); - expect(attributes.attributes, { - 'attr2': 'are', - }); + expect(log, [ + isMethodCall( + 'PerformanceAttributes#removeAttribute', + arguments: { + 'handle': currentTraceHandle, + 'name': name, + }, + ), + ]); }); - test('getAttribute', () { - attributes.putAttribute('attr1', 'apple'); - attributes.putAttribute('attr2', 'are'); - expect(attributes.getAttribute('attr1'), 'apple'); + test('getAttributes', () async { + final Map result = await attributeTrace.getAttributes(); + + expect(log, [ + isMethodCall( + 'PerformanceAttributes#getAttributes', + arguments: {'handle': currentTraceHandle}, + ), + ]); - expect(attributes.getAttribute('attr3'), isNull); + expect(result, getAttributesResult); }); }); });