flutter/packages/flutter_tools/test/utils_test.dart

373 lines
13 KiB
Dart
Raw Normal View History

// Copyright 2016 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:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/utils.dart';
import 'package:flutter_tools/src/base/version.dart';
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
import 'package:flutter_tools/src/base/terminal.dart';
import 'src/common.dart';
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
import 'src/context.dart';
void main() {
group('SettingsFile', () {
test('parse', () {
final SettingsFile file = SettingsFile.parse('''
# ignore comment
foo=bar
baz=qux
''');
expect(file.values['foo'], 'bar');
expect(file.values['baz'], 'qux');
expect(file.values, hasLength(2));
});
});
group('uuid', () {
// xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
test('simple', () {
final Uuid uuid = Uuid();
final String result = uuid.generateV4();
expect(result.length, 36);
expect(result[8], '-');
expect(result[13], '-');
expect(result[18], '-');
expect(result[23], '-');
});
test('can parse', () {
final Uuid uuid = Uuid();
final String result = uuid.generateV4();
expect(int.parse(result.substring(0, 8), radix: 16), isNotNull);
expect(int.parse(result.substring(9, 13), radix: 16), isNotNull);
expect(int.parse(result.substring(14, 18), radix: 16), isNotNull);
expect(int.parse(result.substring(19, 23), radix: 16), isNotNull);
expect(int.parse(result.substring(24, 36), radix: 16), isNotNull);
});
test('special bits', () {
final Uuid uuid = Uuid();
String result = uuid.generateV4();
expect(result[14], '4');
expect(result[19].toLowerCase(), isIn('89ab'));
result = uuid.generateV4();
expect(result[19].toLowerCase(), isIn('89ab'));
result = uuid.generateV4();
expect(result[19].toLowerCase(), isIn('89ab'));
});
test('is pretty random', () {
2019-03-06 19:05:16 +00:00
final Set<String> set = <String>{};
Uuid uuid = Uuid();
for (int i = 0; i < 64; i++) {
final String val = uuid.generateV4();
expect(set, isNot(contains(val)));
set.add(val);
}
uuid = Uuid();
for (int i = 0; i < 64; i++) {
final String val = uuid.generateV4();
expect(set, isNot(contains(val)));
set.add(val);
}
uuid = Uuid();
for (int i = 0; i < 64; i++) {
final String val = uuid.generateV4();
expect(set, isNot(contains(val)));
set.add(val);
}
});
});
group('Version', () {
test('can parse and compare', () {
expect(Version.unknown.toString(), equals('unknown'));
expect(Version(null, null, null).toString(), equals('0'));
final Version v1 = Version.parse('1');
expect(v1.major, equals(1));
expect(v1.minor, equals(0));
expect(v1.patch, equals(0));
expect(v1, greaterThan(Version.unknown));
final Version v2 = Version.parse('1.2');
expect(v2.major, equals(1));
expect(v2.minor, equals(2));
expect(v2.patch, equals(0));
final Version v3 = Version.parse('1.2.3');
expect(v3.major, equals(1));
expect(v3.minor, equals(2));
expect(v3.patch, equals(3));
final Version v4 = Version.parse('1.12');
expect(v4, greaterThan(v2));
expect(v3, greaterThan(v2));
expect(v2, greaterThan(v1));
final Version v5 = Version(1, 2, 0, text: 'foo');
expect(v5, equals(v2));
expect(Version.parse('Preview2.2'), isNull);
});
});
group('Poller', () {
const Duration kShortDelay = Duration(milliseconds: 100);
Poller poller;
tearDown(() {
poller?.cancel();
});
test('fires at start', () async {
bool called = false;
poller = Poller(() async {
called = true;
}, const Duration(seconds: 1));
expect(called, false);
await Future<void>.delayed(kShortDelay);
expect(called, true);
});
test('runs periodically', () async {
// Ensure we get the first (no-delay) callback, and one of the periodic callbacks.
int callCount = 0;
poller = Poller(() async {
callCount++;
}, Duration(milliseconds: kShortDelay.inMilliseconds ~/ 2));
expect(callCount, 0);
await Future<void>.delayed(kShortDelay);
expect(callCount, greaterThanOrEqualTo(2));
});
test('no quicker then the periodic delay', () async {
// Make sure that the poller polls at delay + the time it took to run the callback.
final Completer<Duration> completer = Completer<Duration>();
DateTime firstTime;
poller = Poller(() async {
if (firstTime == null)
firstTime = DateTime.now();
else
completer.complete(DateTime.now().difference(firstTime));
// introduce a delay
await Future<void>.delayed(kShortDelay);
}, kShortDelay);
final Duration duration = await completer.future;
expect(
duration, greaterThanOrEqualTo(Duration(milliseconds: kShortDelay.inMilliseconds * 2)));
});
});
group('Misc', () {
test('snakeCase', () async {
expect(snakeCase('abc'), equals('abc'));
expect(snakeCase('abC'), equals('ab_c'));
expect(snakeCase('aBc'), equals('a_bc'));
expect(snakeCase('aBC'), equals('a_b_c'));
expect(snakeCase('Abc'), equals('abc'));
expect(snakeCase('AbC'), equals('ab_c'));
expect(snakeCase('ABc'), equals('a_bc'));
expect(snakeCase('ABC'), equals('a_b_c'));
});
});
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
group('text wrapping', () {
const int _lineLength = 40;
const String _longLine = 'This is a long line that needs to be wrapped.';
final String _longLineWithNewlines = 'This is a long line with newlines that\n'
'needs to be wrapped.\n\n' +
'0123456789' * 5;
final String _longAnsiLineWithNewlines = '${AnsiTerminal.red}This${AnsiTerminal.resetAll} is a long line with newlines that\n'
'needs to be wrapped.\n\n'
'${AnsiTerminal.green}0123456789${AnsiTerminal.resetAll}' +
'0123456789' * 3 +
'${AnsiTerminal.green}0123456789${AnsiTerminal.resetAll}';
const String _onlyAnsiSequences = '${AnsiTerminal.red}${AnsiTerminal.resetAll}';
final String _indentedLongLineWithNewlines = ' This is an indented long line with newlines that\n'
'needs to be wrapped.\n\tAnd preserves tabs.\n \n ' +
'0123456789' * 5;
const String _shortLine = 'Short line.';
const String _indentedLongLine = ' This is an indented long line that needs to be '
'wrapped and indentation preserved.';
final FakeStdio fakeStdio = FakeStdio();
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
void testWrap(String description, Function body) {
testUsingContext(description, body, overrides: <Type, Generator>{
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
OutputPreferences: () => OutputPreferences(wrapText: true, wrapColumn: _lineLength),
});
}
void testNoWrap(String description, Function body) {
testUsingContext(description, body, overrides: <Type, Generator>{
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
OutputPreferences: () => OutputPreferences(wrapText: false),
});
}
test('does not wrap by default in tests', () {
expect(wrapText(_longLine), equals(_longLine));
});
testNoWrap('can override wrap preference if preference is off', () {
expect(wrapText(_longLine, columnWidth: _lineLength, shouldWrap: true), equals('''
This is a long line that needs to be
wrapped.'''));
});
testWrap('can override wrap preference if preference is on', () {
expect(wrapText(_longLine, shouldWrap: false), equals(_longLine));
});
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
testNoWrap('does not wrap at all if not told to wrap', () {
expect(wrapText(_longLine), equals(_longLine));
});
testWrap('does not wrap short lines.', () {
expect(wrapText(_shortLine, columnWidth: _lineLength), equals(_shortLine));
});
testWrap('able to wrap long lines', () {
expect(wrapText(_longLine, columnWidth: _lineLength), equals('''
This is a long line that needs to be
wrapped.'''));
});
testUsingContext('able to handle dynamically changing terminal column size', () {
fakeStdio.currentColumnSize = 20;
expect(wrapText(_longLine), equals('''
This is a long line
that needs to be
wrapped.'''));
fakeStdio.currentColumnSize = _lineLength;
expect(wrapText(_longLine), equals('''
This is a long line that needs to be
wrapped.'''));
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(wrapText: true),
Stdio: () => fakeStdio,
});
Re-land text wrapping/color PR (#22831) This attempts to re-land #22656. There are two changes from the original: I turned off wrapping completely when not sending output to a terminal. Previously I had defaulted to wrapping at and arbitrary 100 chars in that case, just to keep long messages from being too long, but that turns out the be a bad idea because there are tests that are relying on the specific form of the output. It's also pretty arbitrary, and mostly people sending output to a non-terminal will want unwrapped text. I found a better way to terminate ANSI color/bold sequences, so that they can be embedded within each other without needed quite as complex a dance with removing redundant sequences. As part of these changes, I removed the Logger.supportsColor setter so that the one source of truth for color support is in AnsiTerminal.supportsColor. * Turn on line wrapping again in usage and status messages, adds ANSI color to doctor and analysis messages. (#22656) This turns on text wrapping for usage messages and status messages. When on a terminal, wraps to the width of the terminal. When writing to a non-terminal, wrap lines at a default column width (currently defined to be 100 chars). If --no-wrap is specified, then no wrapping occurs. If --wrap-column is specified, wraps to that column (if --wrap is on). Adds ANSI color to the doctor and analysis output on terminals. This is in this PR with the wrapping, since wrapping needs to know how to count visible characters in the presence of ANSI sequences. (This is just one more step towards re-implementing all of Curses for Flutter. :-)) Will not print ANSI sequences when sent to a non-terminal, or of --no-color is specified. Fixes ANSI color and bold sequences so that they can be combined (bold, colored text), and a small bug in indentation calculation for wrapping. Since wrapping is now turned on, also removed many redundant '\n's in the code.
2018-10-11 01:17:56 +00:00
testWrap('wrap long lines with no whitespace', () {
expect(wrapText('0123456789' * 5, columnWidth: _lineLength), equals('''
0123456789012345678901234567890123456789
0123456789'''));
});
testWrap('refuses to wrap to a column smaller than 10 characters', () {
expect(wrapText('$_longLine ' + '0123456789' * 4, columnWidth: 1), equals('''
This is a
long line
that needs
to be
wrapped.
0123456789
0123456789
0123456789
0123456789'''));
});
testWrap('preserves indentation', () {
expect(wrapText(_indentedLongLine, columnWidth: _lineLength), equals('''
This is an indented long line that
needs to be wrapped and indentation
preserved.'''));
});
testWrap('preserves indentation and stripping trailing whitespace', () {
expect(wrapText('$_indentedLongLine ', columnWidth: _lineLength), equals('''
This is an indented long line that
needs to be wrapped and indentation
preserved.'''));
});
testWrap('wraps text with newlines', () {
expect(wrapText(_longLineWithNewlines, columnWidth: _lineLength), equals('''
This is a long line with newlines that
needs to be wrapped.
0123456789012345678901234567890123456789
0123456789'''));
});
testWrap('wraps text with ANSI sequences embedded', () {
expect(wrapText(_longAnsiLineWithNewlines, columnWidth: _lineLength), equals('''
${AnsiTerminal.red}This${AnsiTerminal.resetAll} is a long line with newlines that
needs to be wrapped.
${AnsiTerminal.green}0123456789${AnsiTerminal.resetAll}012345678901234567890123456789
${AnsiTerminal.green}0123456789${AnsiTerminal.resetAll}'''));
});
testWrap('wraps text with only ANSI sequences', () {
expect(wrapText(_onlyAnsiSequences, columnWidth: _lineLength),
equals('${AnsiTerminal.red}${AnsiTerminal.resetAll}'));
});
testWrap('preserves indentation in the presence of newlines', () {
expect(wrapText(_indentedLongLineWithNewlines, columnWidth: _lineLength), equals('''
This is an indented long line with
newlines that
needs to be wrapped.
\tAnd preserves tabs.
01234567890123456789012345678901234567
890123456789'''));
});
testWrap('removes trailing whitespace when wrapping', () {
expect(wrapText('$_longLine \t', columnWidth: _lineLength), equals('''
This is a long line that needs to be
wrapped.'''));
});
testWrap('honors hangingIndent parameter', () {
expect(wrapText(_longLine, columnWidth: _lineLength, hangingIndent: 6), equals('''
This is a long line that needs to be
wrapped.'''));
});
testWrap('handles hangingIndent with a single unwrapped line.', () {
expect(wrapText(_shortLine, columnWidth: _lineLength, hangingIndent: 6), equals('''
Short line.'''));
});
testWrap('handles hangingIndent with two unwrapped lines and the second is empty.', () {
expect(wrapText('$_shortLine\n', columnWidth: _lineLength, hangingIndent: 6), equals('''
Short line.
'''));
});
testWrap('honors hangingIndent parameter on already indented line.', () {
expect(wrapText(_indentedLongLine, columnWidth: _lineLength, hangingIndent: 6), equals('''
This is an indented long line that
needs to be wrapped and
indentation preserved.'''));
});
testWrap('honors hangingIndent and indent parameters at the same time.', () {
expect(wrapText(_indentedLongLine, columnWidth: _lineLength, indent: 6, hangingIndent: 6), equals('''
This is an indented long line
that needs to be wrapped
and indentation
preserved.'''));
});
testWrap('honors indent parameter on already indented line.', () {
expect(wrapText(_indentedLongLine, columnWidth: _lineLength, indent: 6), equals('''
This is an indented long line
that needs to be wrapped and
indentation preserved.'''));
});
testWrap('honors hangingIndent parameter on already indented line.', () {
expect(wrapText(_indentedLongLineWithNewlines, columnWidth: _lineLength, hangingIndent: 6), equals('''
This is an indented long line with
newlines that
needs to be wrapped.
And preserves tabs.
01234567890123456789012345678901234567
890123456789'''));
});
});
}
class FakeStdio extends Stdio {
FakeStdio();
int currentColumnSize = 20;
@override
int get terminalColumns => currentColumnSize;
}