blob: aceda511064c8f621c7e8d7dd19f05c79cfb1097 [file] [log] [blame]
// 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)';
}
}