blob: daaf198b07447bc0877581a0ea6cf76cc517ff9b [file] [log] [blame]
// Copyright 2018 The Chromium 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 'dart:async';
import 'package:devtools_app/src/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
group('utils', () {
test('printKb', () {
const int kb = 1024;
expect(printKb(0), '0');
expect(printKb(1), '1');
expect(printKb(kb - 1), '1');
expect(printKb(kb), '1');
expect(printKb(kb + 1), '2');
expect(printKb(2000), '2');
});
test('printMb', () {
const int MB = 1024 * 1024;
expect(printMb(10 * MB, 0), '10');
expect(printMb(10 * MB), '10.0');
expect(printMb(10 * MB, 2), '10.00');
expect(printMb(1000 * MB, 0), '1000');
expect(printMb(1000 * MB), '1000.0');
expect(printMb(1000 * MB, 2), '1000.00');
});
test('msAsText', () {
expect(msText(const Duration(microseconds: 3111)), equals('3.1 ms'));
expect(
msText(const Duration(microseconds: 3199), includeUnit: false),
equals('3.2'),
);
expect(
msText(const Duration(microseconds: 3159), fractionDigits: 2),
equals('3.16 ms'),
);
expect(
msText(const Duration(microseconds: 3111), fractionDigits: 3),
equals('3.111 ms'),
);
expect(
msText(const Duration(milliseconds: 3)),
equals('3.0 ms'),
);
});
test('nullSafeMin', () {
expect(nullSafeMin(1, 2), equals(1));
expect(nullSafeMin(1, null), equals(1));
expect(nullSafeMin(null, 2), equals(2));
expect(nullSafeMin(null, null), equals(null));
});
test('nullSafeMin', () {
expect(nullSafeMax(1, 2), equals(2));
expect(nullSafeMax(1, null), equals(1));
expect(nullSafeMax(null, 2), equals(2));
expect(nullSafeMax(null, null), equals(null));
});
test('log2', () {
expect(log2(1), equals(0));
expect(log2(1.5), equals(0));
expect(log2(2), equals(1));
expect(log2(3), equals(1));
expect(log2(4), equals(2));
});
test('executeWithDelay', () async {
const delayMs = 500;
int n = 1;
int start;
int end;
// Condition n >= 2 is false, so we should execute with a delay.
start = DateTime.now().millisecondsSinceEpoch;
executeWithDelay(
const Duration(milliseconds: 500),
() {
n++;
end = DateTime.now().millisecondsSinceEpoch;
},
executeNow: n >= 2,
);
expect(n, equals(1));
expect(end, isNull);
await Future.delayed(const Duration(milliseconds: 250));
expect(n, equals(1));
expect(end, isNull);
await Future.delayed(const Duration(milliseconds: 250));
expect(n, equals(2));
expect(end, isNotNull);
// 1000ms is arbitrary. We want to ensure it doesn't run in less time than
// we requested (checked above), but we don't want to be too strict because
// shared CI CPUs can be slow.
const epsilonMs = 1000;
expect((end - start - delayMs).abs(), lessThan(epsilonMs));
// Condition n >= 2 is true, so we should not execute with a delay.
end = null;
start = DateTime.now().millisecondsSinceEpoch;
executeWithDelay(
const Duration(milliseconds: 500),
() {
n++;
end = DateTime.now().millisecondsSinceEpoch;
},
executeNow: true,
);
expect(n, equals(3));
expect(end, isNotNull);
// 400ms is arbitrary. It is less than 500, which is what matters. This
// can be increased if this test starts to flake.
expect(end - start, lessThan(400));
});
test('timeRange', () {
final timeRange = TimeRange();
expect(timeRange.toString(), equals('[null μs - null μs]'));
timeRange
..start = const Duration(microseconds: 1000)
..end = const Duration(microseconds: 8000);
expect(timeRange.duration.inMicroseconds, equals(7000));
expect(timeRange.toString(), equals('[1000 μs - 8000 μs]'));
expect(
timeRange.toString(unit: TimeUnit.milliseconds),
equals('[1 ms - 8 ms]'),
);
final t = TimeRange()
..start = const Duration(milliseconds: 100)
..end = const Duration(milliseconds: 200);
final overlapBeginning = TimeRange()
..start = const Duration(milliseconds: 50)
..end = const Duration(milliseconds: 150);
final overlapMiddle = TimeRange()
..start = const Duration(milliseconds: 125)
..end = const Duration(milliseconds: 175);
final overlapEnd = TimeRange()
..start = const Duration(milliseconds: 150)
..end = const Duration(milliseconds: 250);
final overlapAll = TimeRange()
..start = const Duration(milliseconds: 50)
..end = const Duration(milliseconds: 250);
final noOverlap = TimeRange()
..start = const Duration(milliseconds: 300)
..end = const Duration(milliseconds: 400);
expect(t.overlaps(t), isTrue);
expect(t.overlaps(overlapBeginning), isTrue);
expect(t.overlaps(overlapMiddle), isTrue);
expect(t.overlaps(overlapEnd), isTrue);
expect(t.overlaps(overlapAll), isTrue);
expect(t.overlaps(noOverlap), isFalse);
});
test('longestFittingSubstring', () {
const asciiStr = 'ComponentElement.performRebuild';
const nonAsciiStr = 'ԪElement.updateChildԪ';
num slowMeasureCallback(_) => 100;
expect(
longestFittingSubstring(
asciiStr,
0,
asciiMeasurements,
slowMeasureCallback,
),
equals(''),
);
expect(
longestFittingSubstring(
asciiStr,
50,
asciiMeasurements,
slowMeasureCallback,
),
equals('Compo'),
);
expect(
longestFittingSubstring(
asciiStr,
224,
asciiMeasurements,
slowMeasureCallback,
),
equals('ComponentElement.performRebuild'),
);
expect(
longestFittingSubstring(
asciiStr,
300,
asciiMeasurements,
slowMeasureCallback,
),
equals('ComponentElement.performRebuild'),
);
expect(nonAsciiStr.codeUnitAt(0), greaterThanOrEqualTo(128));
expect(
longestFittingSubstring(
nonAsciiStr,
99,
asciiMeasurements,
slowMeasureCallback,
),
equals(''),
);
expect(
longestFittingSubstring(
nonAsciiStr,
100,
asciiMeasurements,
slowMeasureCallback,
),
equals('Ԫ'),
);
expect(
longestFittingSubstring(
nonAsciiStr,
230,
asciiMeasurements,
slowMeasureCallback,
),
equals('ԪElement.updateChild'),
);
expect(
longestFittingSubstring(
nonAsciiStr,
329,
asciiMeasurements,
slowMeasureCallback,
),
equals('ԪElement.updateChild'),
);
expect(
longestFittingSubstring(
nonAsciiStr,
330,
asciiMeasurements,
slowMeasureCallback,
),
equals('ԪElement.updateChildԪ'),
);
});
test('isLetter', () {
expect(isLetter('@'.codeUnitAt(0)), isFalse);
expect(isLetter('['.codeUnitAt(0)), isFalse);
expect(isLetter('`'.codeUnitAt(0)), isFalse);
expect(isLetter('{'.codeUnitAt(0)), isFalse);
expect(isLetter('A'.codeUnitAt(0)), isTrue);
expect(isLetter('Z'.codeUnitAt(0)), isTrue);
expect(isLetter('a'.codeUnitAt(0)), isTrue);
expect(isLetter('z'.codeUnitAt(0)), isTrue);
});
test('getSimpleStackFrameName', () {
String name =
'_WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&'
'SchedulerBinding.handleBeginFrame';
expect(
getSimpleStackFrameName(name),
equals('SchedulerBinding.handleBeginFrame'),
);
name =
'_WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&'
'SchedulerBinding.handleBeginFrame.<anonymous closure>';
expect(
getSimpleStackFrameName(name),
equals('SchedulerBinding.handleBeginFrame.<closure>'),
);
name = '__CompactLinkedHashSet&_HashFieldBase&_HashBase&_OperatorEquals'
'AndHashCode&_SetMixin.toList';
expect(getSimpleStackFrameName(name), equals('_SetMixin.toList'));
name = 'ClassName&SuperClassName&\$BadClassName.method';
expect(getSimpleStackFrameName(name), equals('\$BadClassName.method'));
// Ampersand as C++ reference.
name =
'dart::DartEntry::InvokeFunction(dart::Function const&, dart::Array '
'const&, dart::Array const&, unsigned long)';
expect(getSimpleStackFrameName(name), equals(name));
name =
'SkCanvas::drawTextBlob(SkTextBlob const*, float, float, SkPaint const&)';
expect(getSimpleStackFrameName(name), equals(name));
// No leading class names.
name = '_CustomZone.run';
expect(getSimpleStackFrameName(name), equals(name));
});
group('pluralize', () {
test('zero', () {
expect(pluralize('cat', 0), 'cats');
});
test('one', () {
expect(pluralize('cat', 1), 'cat');
});
test('many', () {
expect(pluralize('cat', 2), 'cats');
});
test('irregular plurals', () {
expect(pluralize('index', 1, plural: 'indices'), 'index');
expect(pluralize('index', 2, plural: 'indices'), 'indices');
});
});
group('safeDivide', () {
test('divides a finite result correctly', () {
expect(safeDivide(2.0, 1.0), 2.0);
expect(safeDivide(2, -4), -0.5);
});
test('produces the safe value on nan division', () {
expect(safeDivide(double.nan, 1.0), 0.0);
expect(safeDivide(double.nan, 1.0, ifNotFinite: 50.0), 50.0);
expect(safeDivide(0.0, double.nan, ifNotFinite: -5.0), -5.0);
});
test('produces the safe value on infinite division', () {
expect(safeDivide(double.infinity, 1.0), 0.0);
expect(
safeDivide(
double.nan,
double.negativeInfinity,
ifNotFinite: 50.0,
),
50.0);
});
test('produces the safe value on null division', () {
expect(safeDivide(null, 1.0), 0.0);
expect(safeDivide(1.0, null, ifNotFinite: 50.0), 50.0);
});
test('produces the safe value on division by zero', () {
expect(safeDivide(1.0, 0.0), 0.0);
expect(safeDivide(-50.0, 0.0, ifNotFinite: 10.0), 10.0);
});
});
group('Reporter', () {
int called = 0;
Reporter reporter;
void call() {
called++;
}
setUp(() {
called = 0;
reporter = Reporter();
});
test('notifies listeners', () {
expect(reporter.hasListeners, false);
reporter.addListener(call);
expect(called, 0);
expect(reporter.hasListeners, true);
reporter.notify();
expect(called, 1);
reporter.notify();
reporter.notify();
expect(called, 3);
reporter.removeListener(call);
expect(called, 3);
});
test('notifies multiple listeners', () {
reporter.addListener(() => called++);
reporter.addListener(() => called++);
reporter.addListener(() => called++);
reporter.notify();
expect(called, 3);
// Note that because we passed in anonymous callbacks, there's no way
// to remove them.
});
test('deduplicates listeners', () {
reporter.addListener(call);
reporter.addListener(call);
reporter.notify();
expect(called, 1);
reporter.removeListener(call);
reporter.notify();
expect(called, 1);
});
test('safely removes multiple times', () {
reporter.removeListener(call);
reporter.addListener(call);
reporter.notify();
expect(called, 1);
reporter.removeListener(call);
reporter.removeListener(call);
reporter.notify();
expect(called, 1);
});
});
group('ValueReporter', () {
int called = 0;
void call() {
called++;
}
ValueReporter<String> reporter;
setUp(() {
reporter = ValueReporter(null);
});
test('notifies listeners', () {
expect(reporter.hasListeners, false);
reporter.addListener(call);
expect(called, 0);
expect(reporter.hasListeners, true);
reporter.value = 'first call';
expect(called, 1);
reporter.value = 'second call';
reporter.value = 'third call';
expect(called, 3);
reporter.removeListener(call);
reporter.value = 'fourth call';
expect(called, 3);
});
});
group('SafeAccess', () {
test('safeFirst', () {
final list = <int>[];
final Iterable<int> iterable = list;
expect(list.safeFirst, isNull);
expect(iterable.safeFirst, isNull);
list.addAll([1, 2, 3]);
expect(list.safeFirst, equals(1));
expect(iterable.safeFirst, equals(1));
list.insert(0, null);
expect(list.safeFirst, isNull);
expect(iterable.safeFirst, isNull);
});
test('safeLast', () {
final list = <int>[];
expect(list.safeLast, isNull);
list.addAll([1, 2, 3]);
expect(list.safeLast, equals(3));
list.add(null);
expect(list.safeLast, isNull);
});
test('safeGet', () {
final list = <int>[];
expect(list.safeGet(0), isNull);
list.addAll([1, 2]);
expect(list.safeGet(0), equals(1));
expect(list.safeGet(1), equals(2));
expect(list.safeGet(-1), isNull);
});
});
});
group('LogicalKeySetExtension', () {
testWidgets('meta non-mac', (WidgetTester tester) async {
final keySet =
LogicalKeySet(LogicalKeyboardKey.meta, LogicalKeyboardKey.keyP);
expect(keySet.describeKeys(), 'Meta-P');
});
testWidgets('meta mac', (WidgetTester tester) async {
final keySet =
LogicalKeySet(LogicalKeyboardKey.meta, LogicalKeyboardKey.keyP);
expect(keySet.describeKeys(isMacOS: true), '⌘P');
});
testWidgets('ctrl', (WidgetTester tester) async {
final keySet =
LogicalKeySet(LogicalKeyboardKey.control, LogicalKeyboardKey.keyP);
expect(keySet.describeKeys(), 'Control-P');
});
});
}
// This was generated from a canvas with font size 14.0.
const asciiMeasurements = [
0,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
0,
3.8896484375,
3.8896484375,
3.8896484375,
3.8896484375,
3.8896484375,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
4.6619873046875,
0,
4.6619873046875,
4.6619873046875,
3.8896484375,
3.8896484375,
4.9697265625,
7.7861328125,
7.7861328125,
12.4482421875,
9.337890625,
2.6728515625,
4.662109375,
4.662109375,
5.4482421875,
8.17578125,
3.8896484375,
4.662109375,
3.8896484375,
3.8896484375,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
3.8896484375,
3.8896484375,
8.17578125,
8.17578125,
8.17578125,
7.7861328125,
14.2119140625,
9.337890625,
9.337890625,
10.1103515625,
10.1103515625,
9.337890625,
8.5517578125,
10.8896484375,
10.1103515625,
3.8896484375,
7,
9.337890625,
7.7861328125,
11.662109375,
10.1103515625,
10.8896484375,
9.337890625,
10.8896484375,
10.1103515625,
9.337890625,
8.5517578125,
10.1103515625,
9.337890625,
13.2138671875,
9.337890625,
9.337890625,
8.5517578125,
3.8896484375,
3.8896484375,
3.8896484375,
6.5693359375,
7.7861328125,
4.662109375,
7.7861328125,
7.7861328125,
7,
7.7861328125,
7.7861328125,
3.8896484375,
7.7861328125,
7.7861328125,
3.1103515625,
3.1103515625,
7,
3.1103515625,
11.662109375,
7.7861328125,
7.7861328125,
7.7861328125,
7.7861328125,
4.662109375,
7,
3.8896484375,
7.7861328125,
7,
10.1103515625,
7,
7,
7,
4.67578125,
3.63671875,
4.67578125,
8.17578125,
0,
];