blob: 7b0770b9f9ceab7f27a65d5679610476a811dbe0 [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. 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:convert';
import 'package:path/path.dart' as p;
import 'package:test/test.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;
import '../utils.dart';
void main() {
compileFormatter();
group('given file paths', () {
test('formats a directory', () async {
await d.dir('code', [
d.file('a.dart', unformattedSource),
d.file('b.dart', formattedSource),
d.file('c.dart', unformattedSource),
]).create();
var process = await runFormatterOnDir();
await expectLater(
process.stdout,
emitsInOrder([
'Formatted ${p.join('code', 'a.dart')}',
'Formatted ${p.join('code', 'c.dart')}',
]),
);
await expectLater(
process.stdout,
emits(startsWith('Formatted 3 files (2 changed)')),
);
await process.shouldExit(0);
// Overwrites the files.
await d.dir('code', [d.file('a.dart', formattedSource)]).validate();
await d.dir('code', [d.file('c.dart', formattedSource)]).validate();
});
test('formats multiple paths', () async {
await d.dir('code', [
d.dir('subdir', [d.file('a.dart', unformattedSource)]),
d.file('b.dart', unformattedSource),
d.file('c.dart', unformattedSource),
]).create();
var process = await runFormatter([
p.join('code', 'subdir'),
p.join('code', 'c.dart'),
]);
await expectLater(
process.stdout,
emitsInOrder([
'Formatted ${p.join('code', 'subdir', 'a.dart')}',
'Formatted ${p.join('code', 'c.dart')}',
]),
);
await expectLater(
process.stdout,
emits(startsWith('Formatted 2 files (2 changed)')),
);
await process.shouldExit(0);
// Overwrites the selected files.
await d.dir('code', [
d.dir('subdir', [d.file('a.dart', formattedSource)]),
d.file('b.dart', unformattedSource),
d.file('c.dart', formattedSource),
]).validate();
});
});
test('exits with 64 on a command line argument error', () async {
var process = await runFormatter(['-wat']);
await process.shouldExit(64);
});
test('exits with 65 on a parse error', () async {
await d.dir('code', [d.file('a.dart', 'herp derp i are a dart')]).create();
var process = await runFormatterOnDir();
await process.shouldExit(65);
});
test('--version prints the version number', () async {
var process = await runFormatter(['--version']);
// Match something roughly semver-like.
await expectLater(
process.stdout,
emits(matches(RegExp(r'\d+\.\d+\.\d+.*'))),
);
await process.shouldExit(0);
});
group('--help', () {
test('non-verbose shows description and common options', () async {
var process = await runFormatter(['--help']);
await expectLater(
process.stdout,
emits('Idiomatically format Dart source code.'),
);
await expectLater(process.stdout, emitsThrough(contains('-o, --output')));
await expectLater(process.stdout, neverEmits(contains('--summary')));
await process.shouldExit(0);
});
test('verbose shows description and all options', () async {
var process = await runFormatter(['--help', '--verbose']);
await expectLater(
process.stdout,
emits('Idiomatically format Dart source code.'),
);
await expectLater(process.stdout, emitsThrough(contains('-o, --output')));
await expectLater(process.stdout, emitsThrough(contains('--show')));
await expectLater(process.stdout, emitsThrough(contains('--summary')));
await process.shouldExit(0);
});
});
test('--verbose errors if not used with --help', () async {
var process = await runFormatterOnDir(['--verbose']);
expect(await process.stderr.next, 'Can only use --verbose with --help.');
await process.shouldExit(64);
});
group('--indent', () {
test('sets the leading indentation of the output', () async {
var process = await runFormatter(['--indent=3']);
process.stdin.writeln("main() {'''");
process.stdin.writeln("a flush left multi-line string''';}");
await process.stdin.close();
await expectLater(
process.stdout,
emitsInOrder([
' main() {',
" '''",
"a flush left multi-line string''';",
' }',
]),
);
await process.shouldExit(0);
});
test('errors if the indent is not a non-negative number', () async {
var process = await runFormatter(['--indent=notanum']);
await process.shouldExit(64);
process = await runFormatter(['--indent=-4']);
await process.shouldExit(64);
});
});
group('--set-exit-if-changed', () {
test('gives exit code 0 if there are no changes', () async {
await d.dir('code', [d.file('a.dart', formattedSource)]).create();
var process = await runFormatterOnDir(['--set-exit-if-changed']);
await process.shouldExit(0);
});
test('gives exit code 1 if there are changes', () async {
await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
var process = await runFormatterOnDir(['--set-exit-if-changed']);
await process.shouldExit(1);
});
test('gives exit code 1 if there are changes when not writing', () async {
await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
var process = await runFormatterOnDir([
'--set-exit-if-changed',
'--show=none',
]);
await process.shouldExit(1);
});
});
group('--selection', () {
test('errors if given path', () async {
var process = await runFormatter(['--selection', 'path']);
await process.shouldExit(64);
});
test('errors on wrong number of components', () async {
var process = await runFormatter(['--selection', '1']);
await process.shouldExit(64);
process = await runFormatter(['--selection', '1:2:3']);
await process.shouldExit(64);
});
test('errors on non-integer component', () async {
var process = await runFormatter(['--selection', '1:2.3']);
await process.shouldExit(64);
});
test('updates selection', () async {
var process = await runFormatter(['--output=json', '--selection=6:10']);
process.stdin.writeln(unformattedSource);
await process.stdin.close();
var json = jsonEncode({
'path': 'stdin',
'source': formattedSource,
'selection': {'offset': 5, 'length': 9},
});
await expectLater(process.stdout, emits(json));
await process.shouldExit();
});
});
group('--enable-experiment', () {
test('passes experiment flags to parser', () async {
var process = await runFormatter([
'--enable-experiment=test-experiment,variance',
]);
process.stdin.writeln('class Writer<in T> {}');
await process.stdin.close();
// The formatter doesn't actually support formatting variance annotations,
// but we want to test that the experiment flags are passed all the way
// to the parser, so just test that it parses the variance annotation
// without errors and then fails to format.
expect(
await process.stderr.next,
'Hit a bug in the formatter when formatting stdin.',
);
expect(
await process.stderr.next,
'Please report at: github.com/dart-lang/dart_style/issues',
);
expect(
await process.stderr.next,
'The formatter produced unexpected output. Input was:',
);
expect(await process.stderr.next, 'class Writer<in T> {}');
expect(await process.stderr.next, '');
expect(await process.stderr.next, 'Which formatted to:');
expect(await process.stderr.next, 'class Writer<T> {}');
await process.shouldExit(70);
});
});
}