import 'package:ui/ui.dart' as ui;
import 'package:ui/src/engine.dart';
import 'package:test/test.dart';
final ui.ParagraphStyle ahemStyle = ui.ParagraphStyle(
fontFamily: 'ahem',
fontSize: 10,
);
const ui.ParagraphConstraints constraints = ui.ParagraphConstraints(width: 50);
const ui.ParagraphConstraints infiniteConstraints =
ui.ParagraphConstraints(width: double.infinity);
ui.Paragraph build(ui.ParagraphStyle style, String text) {
var builder = ui.ParagraphBuilder(style);
builder.addText(text);
return builder.build();
}
typedef MeasurementTestBody = void Function(TextMeasurementService instance);
void testMeasurements(String description, MeasurementTestBody body) {
test(
'$description (dom)',
() => body(TextMeasurementService.domInstance),
);
test(
'$description (canvas)',
() => body(TextMeasurementService.canvasInstance),
);
}
void main() {
group('$RulerManager', () {
ui.ParagraphStyle s1 = ui.ParagraphStyle(fontFamily: 'sans-serif');
ui.ParagraphStyle s2 = ui.ParagraphStyle(
fontWeight: ui.FontWeight.bold,
);
ui.ParagraphStyle s3 = ui.ParagraphStyle(fontSize: 22.0);
ParagraphGeometricStyle style1, style2, style3;
ui.Paragraph style1Text1, style1Text2;
ui.Paragraph style2Text1, style3Text3;
setUp(() {
style1Text1 = build(s1, '1');
style1Text2 = build(s1, '2');
style2Text1 = build(s2, '1');
style3Text3 = build(s3, '3');
style1 = style1Text1.webOnlyGetParagraphGeometricStyle();
style2 = style2Text1.webOnlyGetParagraphGeometricStyle();
style3 = style3Text3.webOnlyGetParagraphGeometricStyle();
ParagraphGeometricStyle style1_2 =
style1Text2.webOnlyGetParagraphGeometricStyle();
expect(style1_2, style1);
});
test('caches rulers', () {
final RulerManager rulerManager = RulerManager(rulerCacheCapacity: 2);
ParagraphRuler ruler1, ruler2, ruler3;
expect(rulerManager.rulerCacheCapacity, 2);
expect(rulerManager.rulers.length, 0);
ruler1 = rulerManager.findOrCreateRuler(style1);
expect(rulerManager.rulers.length, 1);
expect(ruler1.hitCount, 1);
ruler1 = rulerManager.findOrCreateRuler(style1);
expect(rulerManager.rulers.length, 1);
expect(ruler1.hitCount, 2);
rulerManager.findOrCreateRuler(style1);
expect(rulerManager.rulers.length, 1);
expect(ruler1.hitCount, 3);
ruler2 = rulerManager.findOrCreateRuler(style2);
expect(rulerManager.rulers.length, 2);
expect(ruler1.hitCount, 3);
expect(ruler2.hitCount, 1);
rulerManager.findOrCreateRuler(style2);
rulerManager.findOrCreateRuler(style2);
rulerManager.findOrCreateRuler(style2);
expect(rulerManager.rulers.length, 2);
expect(ruler2.hitCount, 4);
ruler3 = rulerManager.findOrCreateRuler(style3);
expect(rulerManager.rulers.length, 3);
expect(ruler1.hitCount, 3);
expect(ruler2.hitCount, 4);
expect(ruler3.hitCount, 1);
expect(ruler3.debugIsDisposed, isFalse);
rulerManager.cleanUpRulerCache();
expect(rulerManager.rulers.length, 2);
expect(rulerManager.rulers, containsValue(ruler1));
expect(rulerManager.rulers, containsValue(ruler2));
expect(rulerManager.rulers, isNot(containsValue(ruler3)));
expect(ruler1.debugIsDisposed, isFalse);
expect(ruler2.debugIsDisposed, isFalse);
expect(ruler3.debugIsDisposed, isTrue);
ruler1 = rulerManager.rulers[style1];
expect(ruler1.style, style1);
expect(ruler1.hitCount, 0);
ruler2 = rulerManager.rulers[style2];
expect(ruler2.style, style2);
expect(ruler2.hitCount, 0);
});
});
group('$TextMeasurementService', () {
setUp(() {
TextMeasurementService.initialize(rulerCacheCapacity: 2);
});
tearDown(() {
TextMeasurementService.clearCache();
});
testMeasurements(
'preserves whitespace when measuring',
(TextMeasurementService instance) {
ui.Paragraph text;
MeasurementResult result;
text = build(ahemStyle, ' abc');
result = instance.measure(text, infiniteConstraints);
expect(result.maxIntrinsicWidth, 60);
expect(result.minIntrinsicWidth, 30);
expect(result.height, 10);
text = build(ahemStyle, 'abc ');
result = instance.measure(text, infiniteConstraints);
expect(result.maxIntrinsicWidth, 60);
expect(result.minIntrinsicWidth, 30);
expect(result.height, 10);
text = build(ahemStyle, ' ab c ');
result = instance.measure(text, infiniteConstraints);
expect(result.maxIntrinsicWidth, 100);
expect(result.minIntrinsicWidth, 20);
expect(result.height, 10);
text = build(ahemStyle, ' ');
result = instance.measure(text, infiniteConstraints);
expect(result.maxIntrinsicWidth, 10);
expect(result.minIntrinsicWidth, 0);
expect(result.height, 10);
text = build(ahemStyle, ' ');
result = instance.measure(text, infiniteConstraints);
expect(result.maxIntrinsicWidth, 50);
expect(result.minIntrinsicWidth, 0);
expect(result.height, 10);
},
);
testMeasurements(
'uses single-line when text can fit without wrapping',
(TextMeasurementService instance) {
final MeasurementResult result =
instance.measure(build(ahemStyle, '12345'), constraints);
expect(result.isSingleLine, true);
expect(result.maxIntrinsicWidth, 50);
expect(result.minIntrinsicWidth, 50);
expect(result.width, 50);
expect(result.height, 10);
},
);
testMeasurements(
'uses multi-line for long text',
(TextMeasurementService instance) {
final MeasurementResult result =
instance.measure(build(ahemStyle, '1234567890'), constraints);
expect(result.isSingleLine, false);
expect(result.maxIntrinsicWidth, 100);
expect(result.minIntrinsicWidth, 100);
expect(result.width, 50);
expect(result.height, 20);
},
);
testMeasurements(
'uses multi-line for text that contains new-line',
(TextMeasurementService instance) {
final MeasurementResult result =
instance.measure(build(ahemStyle, '12\n34'), constraints);
expect(result.isSingleLine, false);
expect(result.maxIntrinsicWidth, 20);
expect(result.minIntrinsicWidth, 20);
expect(result.width, 50);
expect(result.height, 20);
},
);
testMeasurements('empty lines', (TextMeasurementService instance) {
MeasurementResult result;
result = instance.measure(build(ahemStyle, '\n\n1234'), constraints);
expect(result.maxIntrinsicWidth, 40);
expect(result.minIntrinsicWidth, 40);
expect(result.height, 30);
result = instance.measure(build(ahemStyle, '12\n\n345'), constraints);
expect(result.maxIntrinsicWidth, 30);
expect(result.minIntrinsicWidth, 30);
expect(result.height, 30);
if (instance is CanvasTextMeasurementService) {
result = instance.measure(build(ahemStyle, '1234\n\n'), constraints);
expect(result.maxIntrinsicWidth, 40);
expect(result.minIntrinsicWidth, 40);
expect(result.height, 30);
}
});
test('takes letter spacing into account', () {
final constraints = ui.ParagraphConstraints(width: 100);
final normalBuilder = ui.ParagraphBuilder(ahemStyle);
normalBuilder.addText('abc');
final normalText = normalBuilder.build();
final spacedBuilder = ui.ParagraphBuilder(ahemStyle);
spacedBuilder.pushStyle(ui.TextStyle(letterSpacing: 1.5));
spacedBuilder.addText('abc');
final spacedText = spacedBuilder.build();
final TextMeasurementService instance =
TextMeasurementService.forParagraph(spacedText);
expect(instance, isInstanceOf<DomTextMeasurementService>());
final normalResult = instance.measure(normalText, constraints);
final spacedResult = instance.measure(spacedText, constraints);
expect(
normalResult.maxIntrinsicWidth < spacedResult.maxIntrinsicWidth,
isTrue,
);
});
test('takes word spacing into account', () {
final constraints = ui.ParagraphConstraints(width: 100);
final normalBuilder = ui.ParagraphBuilder(ahemStyle);
normalBuilder.addText('a b c');
final normalText = normalBuilder.build();
final spacedBuilder = ui.ParagraphBuilder(ahemStyle);
spacedBuilder.pushStyle(ui.TextStyle(wordSpacing: 1.5));
spacedBuilder.addText('a b c');
final spacedText = spacedBuilder.build();
final TextMeasurementService instance =
TextMeasurementService.forParagraph(spacedText);
expect(instance, isInstanceOf<DomTextMeasurementService>());
final normalResult = instance.measure(normalText, constraints);
final spacedResult = instance.measure(spacedText, constraints);
expect(
normalResult.maxIntrinsicWidth < spacedResult.maxIntrinsicWidth,
isTrue,
);
});
testMeasurements('minIntrinsicWidth', (TextMeasurementService instance) {
MeasurementResult result;
result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints);
expect(result.minIntrinsicWidth, 40);
result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints);
expect(result.minIntrinsicWidth, 40);
result = instance.measure(build(ahemStyle, 'abcd efg'), constraints);
expect(result.minIntrinsicWidth, 40);
result = instance.measure(build(ahemStyle, 'abc \ndefg'), constraints);
expect(result.minIntrinsicWidth, 40);
result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints);
expect(result.minIntrinsicWidth, 120);
});
testMeasurements('maxIntrinsicWidth', (TextMeasurementService instance) {
MeasurementResult result;
result = instance.measure(build(ahemStyle, 'abc de fghi'), constraints);
expect(result.maxIntrinsicWidth, 110);
result = instance.measure(build(ahemStyle, 'abcd\nef\nghi'), constraints);
expect(result.maxIntrinsicWidth, 40);
result = instance.measure(build(ahemStyle, 'abcd efg'), constraints);
expect(result.maxIntrinsicWidth, 100);
result = instance.measure(build(ahemStyle, 'abc def '), constraints);
expect(result.maxIntrinsicWidth, 100);
result = instance.measure(build(ahemStyle, 'abc \ndef '), constraints);
expect(result.maxIntrinsicWidth, 60);
result = instance.measure(build(ahemStyle, 'AAAAAAAAAAAA'), constraints);
expect(result.maxIntrinsicWidth, 120);
});
test('respects text overflow', () {
final TextMeasurementService instance =
TextMeasurementService.domInstance;
final overflowStyle = ui.ParagraphStyle(
fontFamily: 'ahem',
fontSize: 10,
ellipsis: '...',
);
final constraints = ui.ParagraphConstraints(width: 50);
MeasurementResult result;
final longText = build(
overflowStyle,
'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
);
result = instance.measure(longText, constraints);
expect(result.height, 10);
final longTextShortPrefix = build(
overflowStyle,
'AAA\nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
);
result = instance.measure(longTextShortPrefix, constraints);
expect(result.height, 20);
});
test('respects max lines', () {
final TextMeasurementService instance =
TextMeasurementService.domInstance;
final maxlinesStyle = ui.ParagraphStyle(
fontFamily: 'ahem',
fontSize: 10,
maxLines: 2,
);
MeasurementResult result;
final oneline = build(maxlinesStyle, 'One line');
result = instance.measure(oneline, infiniteConstraints);
expect(result.height, 10);
});
test('canvas line breaks', () {
});
});
}