| // Copyright 2013 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:js/js.dart'; |
| import 'package:test/bootstrap/browser.dart'; |
| import 'package:test/test.dart'; |
| import 'package:ui/src/engine.dart'; |
| |
| import '../spy.dart'; |
| |
| @JS('window._flutter_internal_on_benchmark') |
| external set onBenchmark (Object? object); |
| |
| void main() { |
| internalBootstrapBrowserTest(() => testMain); |
| } |
| |
| void testMain() { |
| group('$Profiler', () { |
| _profilerTests(); |
| }); |
| |
| group('$Instrumentation', () { |
| _instrumentationTests(); |
| }); |
| } |
| |
| void _profilerTests() { |
| setUp(() { |
| Profiler.isBenchmarkMode = true; |
| Profiler.ensureInitialized(); |
| }); |
| |
| tearDown(() { |
| onBenchmark = null; |
| Profiler.isBenchmarkMode = false; |
| }); |
| |
| test('works when there is no listener', () { |
| expect(() => Profiler.instance.benchmark('foo', 123), returnsNormally); |
| }); |
| |
| test('can listen to benchmarks', () { |
| final List<BenchmarkDatapoint> data = <BenchmarkDatapoint>[]; |
| onBenchmark = allowInterop((String name, num value) { |
| data.add(BenchmarkDatapoint(name, value)); |
| }); |
| |
| Profiler.instance.benchmark('foo', 123); |
| expect(data, <BenchmarkDatapoint>[BenchmarkDatapoint('foo', 123)]); |
| data.clear(); |
| |
| Profiler.instance.benchmark('bar', 0.0125); |
| expect(data, <BenchmarkDatapoint>[BenchmarkDatapoint('bar', 0.0125)]); |
| data.clear(); |
| |
| // Remove listener and make sure nothing breaks and the data isn't being |
| // sent to the old callback anymore. |
| onBenchmark = null; |
| expect(() => Profiler.instance.benchmark('baz', 99.999), returnsNormally); |
| expect(data, isEmpty); |
| }); |
| |
| test('throws on wrong listener type', () { |
| final List<BenchmarkDatapoint> data = <BenchmarkDatapoint>[]; |
| |
| // Wrong callback signature. |
| onBenchmark = allowInterop((num value) { |
| data.add(BenchmarkDatapoint('bad', value)); |
| }); |
| expect( |
| () => Profiler.instance.benchmark('foo', 123), |
| |
| // dart2js throws a NoSuchMethodError, dart2wasm throws a TypeError here. |
| // Just make sure it throws an error in this case. |
| throwsA(isA<Error>()), |
| ); |
| expect(data, isEmpty); |
| |
| // Not even a callback. |
| onBenchmark = 'string'; |
| expect( |
| () => Profiler.instance.benchmark('foo', 123), |
| throwsA(isA<TypeError>()), |
| ); |
| }); |
| } |
| |
| void _instrumentationTests() { |
| setUp(() { |
| Instrumentation.enabled = false; |
| }); |
| |
| tearDown(() { |
| Instrumentation.enabled = false; |
| }); |
| |
| test('when disabled throws instead of initializing', () { |
| expect(() => Instrumentation.instance, throwsStateError); |
| }); |
| |
| test('when disabled throws instead of incrementing counter', () { |
| Instrumentation.enabled = true; |
| final Instrumentation instrumentation = Instrumentation.instance; |
| Instrumentation.enabled = false; |
| expect(() => instrumentation.incrementCounter('test'), throwsStateError); |
| }); |
| |
| test('when enabled increments counter', () { |
| final ZoneSpy spy = ZoneSpy(); |
| spy.run(() { |
| Instrumentation.enabled = true; |
| final Instrumentation instrumentation = Instrumentation.instance; |
| expect(instrumentation.debugPrintTimer, isNull); |
| instrumentation.incrementCounter('foo'); |
| expect(instrumentation.debugPrintTimer, isNotNull); |
| instrumentation.incrementCounter('foo'); |
| instrumentation.incrementCounter('bar'); |
| expect(spy.printLog, isEmpty); |
| |
| expect(instrumentation.debugPrintTimer, isNotNull); |
| spy.fakeAsync.elapse(const Duration(seconds: 2)); |
| expect(instrumentation.debugPrintTimer, isNull); |
| expect(spy.printLog, hasLength(1)); |
| expect( |
| spy.printLog.single, |
| 'Engine counters:\n' |
| ' bar: 1\n' |
| ' foo: 2\n', |
| ); |
| }); |
| }); |
| } |
| |
| class BenchmarkDatapoint { |
| BenchmarkDatapoint(this.name, this.value); |
| |
| final String name; |
| final num value; |
| |
| @override |
| int get hashCode => Object.hash(name, value); |
| |
| @override |
| bool operator ==(Object other) { |
| if (identical(this, other)) { |
| return true; |
| } |
| if (other.runtimeType != runtimeType) { |
| return false; |
| } |
| return other is BenchmarkDatapoint |
| && other.name == name |
| && other.value == value; |
| } |
| |
| @override |
| String toString() { |
| return '$runtimeType("$name", $value)'; |
| } |
| } |