blob: c33771fac09a68a49f9ddfd8560848523c2bf737 [file] [log] [blame]
// Copyright (c) 2014, 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() {
compileFormatterExecutable();
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, emits(startsWith('Formatting directory')));
// Prints the formatting result.
await expectLater(process.stdout, emits(formattedOutput));
await expectLater(process.stdout, emits(formattedOutput));
await expectLater(process.stdout, emits(formattedOutput));
await process.shouldExit(0);
// Does not overwrite by default.
await d.dir('code', [d.file('a.dart', unformattedSource)]).validate();
await d.dir('code', [d.file('c.dart', unformattedSource)]).validate();
});
test('Overwrites files', () 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(['--overwrite']);
await expectLater(
process.stdout, emits(startsWith('Formatting directory')));
// Prints whether each file was changed.
await expectLater(
process.stdout, emits('Formatted ${p.join('code', 'a.dart')}'));
await expectLater(
process.stdout, emits('Unchanged ${p.join('code', 'b.dart')}'));
await expectLater(
process.stdout, emits('Formatted ${p.join('code', 'c.dart')}'));
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('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('errors if --dry-run and --overwrite are both passed', () async {
var process = await runFormatter(['--dry-run', '--overwrite']);
await process.shouldExit(64);
});
test('errors if --dry-run and --machine are both passed', () async {
var process = await runFormatter(['--dry-run', '--machine']);
await process.shouldExit(64);
});
test('errors if --machine and --overwrite are both passed', () async {
var process = await runFormatter(['--machine', '--overwrite']);
await process.shouldExit(64);
});
test('errors if --dry-run and --machine are both passed', () async {
var process = await runFormatter(['--dry-run', '--machine']);
await process.shouldExit(64);
});
test('errors if --machine and --overwrite are both passed', () async {
var process = await runFormatter(['--machine', '--overwrite']);
await process.shouldExit(64);
});
test('errors if --stdin-name and a path are both passed', () async {
var process = await runFormatter(['--stdin-name=name', 'path.dart']);
await process.shouldExit(64);
});
test('--version prints the version number', () async {
var process = await runFormatter(['--version']);
// Match something roughly semver-like.
expect(await process.stdout.next, 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, emits(''));
await expectLater(process.stdout,
emits('Usage: dartfmt [options...] [files or directories...]'));
await expectLater(process.stdout, emitsThrough(contains('--overwrite')));
await expectLater(
process.stdout, emitsThrough(contains('--set-exit-if-changed')));
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, emits(''));
await expectLater(process.stdout,
emits('Usage: dartfmt [options...] [files or directories...]'));
await expectLater(process.stdout, emitsThrough(contains('--overwrite')));
await expectLater(
process.stdout, emitsThrough(contains('--set-exit-if-changed')));
await expectLater(process.stdout, emitsThrough(contains('--fix')));
await process.shouldExit(0);
});
});
test('--verbose errors if not used with --help', () async {
var process = await runFormatterOnDir(['--verbose']);
expect(await process.stdout.next, 'Can only use --verbose with --help.');
await process.shouldExit(64);
});
test('only prints a hidden directory once', () async {
await d.dir('code', [
d.dir('.skip', [
d.file('a.dart', unformattedSource),
d.file('b.dart', unformattedSource)
])
]).create();
var process = await runFormatterOnDir();
expect(await process.stdout.next, startsWith('Formatting directory'));
expect(await process.stdout.next,
"Skipping hidden path ${p.join("code", ".skip")}");
await process.shouldExit();
});
group('--dry-run', () {
test('prints names of files that would change', () async {
await d.dir('code', [
d.file('a_bad.dart', unformattedSource),
d.file('b_good.dart', formattedSource),
d.file('c_bad.dart', unformattedSource),
d.file('d_good.dart', formattedSource)
]).create();
var aBad = p.join('code', 'a_bad.dart');
var cBad = p.join('code', 'c_bad.dart');
var process = await runFormatterOnDir(['--dry-run']);
// The order isn't specified.
expect(await process.stdout.next, anyOf(aBad, cBad));
expect(await process.stdout.next, anyOf(aBad, cBad));
await process.shouldExit();
});
test('does not modify files', () async {
await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
var process = await runFormatterOnDir(['--dry-run']);
expect(await process.stdout.next, p.join('code', 'a.dart'));
await process.shouldExit();
await d.dir('code', [d.file('a.dart', unformattedSource)]).validate();
});
});
group('--machine', () {
test('writes each output as json', () async {
await d.dir('code', [
d.file('a.dart', unformattedSource),
d.file('b.dart', unformattedSource)
]).create();
var jsonA = jsonEncode({
'path': p.join('code', 'a.dart'),
'source': formattedSource,
'selection': {'offset': -1, 'length': -1}
});
var jsonB = jsonEncode({
'path': p.join('code', 'b.dart'),
'source': formattedSource,
'selection': {'offset': -1, 'length': -1}
});
var process = await runFormatterOnDir(['--machine']);
// The order isn't specified.
expect(await process.stdout.next, anyOf(jsonA, jsonB));
expect(await process.stdout.next, anyOf(jsonA, jsonB));
await process.shouldExit();
});
});
group('--preserve', () {
test('errors if given paths', () async {
var process = await runFormatter(['--preserve', 'path', 'another']);
await process.shouldExit(64);
});
test('errors on wrong number of components', () async {
var process = await runFormatter(['--preserve', '1']);
await process.shouldExit(64);
process = await runFormatter(['--preserve', '1:2:3']);
await process.shouldExit(64);
});
test('errors on non-integer component', () async {
var process = await runFormatter(['--preserve', '1:2.3']);
await process.shouldExit(64);
});
test('updates selection', () async {
var process = await runFormatter(['--preserve', '6:10', '-m']);
process.stdin.writeln(unformattedSource);
await process.stdin.close();
var json = jsonEncode({
'path': 'stdin',
'source': formattedSource,
'selection': {'offset': 5, 'length': 9}
});
expect(await process.stdout.next, json);
await process.shouldExit();
});
});
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();
expect(await process.stdout.next, ' main() {');
expect(await process.stdout.next, " '''");
expect(await process.stdout.next, "a flush left multi-line string''';");
expect(await process.stdout.next, ' }');
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 even in dry run', () async {
await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
var process =
await runFormatterOnDir(['--set-exit-if-changed', '--dry-run']);
await process.shouldExit(1);
});
});
group('fix', () {
test('--fix applies all fixes', () async {
var process = await runFormatter(['--fix']);
process.stdin.writeln('foo({a:1}) {');
process.stdin.writeln(' new Bar(const Baz(const []));}');
await process.stdin.close();
expect(await process.stdout.next, 'foo({a = 1}) {');
expect(await process.stdout.next, ' Bar(const Baz([]));');
expect(await process.stdout.next, '}');
await process.shouldExit(0);
});
test('--fix-named-default-separator', () async {
var process = await runFormatter(['--fix-named-default-separator']);
process.stdin.writeln('foo({a:1}) {');
process.stdin.writeln(' new Bar();}');
await process.stdin.close();
expect(await process.stdout.next, 'foo({a = 1}) {');
expect(await process.stdout.next, ' new Bar();');
expect(await process.stdout.next, '}');
await process.shouldExit(0);
});
test('--fix-optional-const', () async {
var process = await runFormatter(['--fix-optional-const']);
process.stdin.writeln('foo({a:1}) {');
process.stdin.writeln(' const Bar(const Baz());}');
await process.stdin.close();
expect(await process.stdout.next, 'foo({a: 1}) {');
expect(await process.stdout.next, ' const Bar(Baz());');
expect(await process.stdout.next, '}');
await process.shouldExit(0);
});
test('--fix-optional-new', () async {
var process = await runFormatter(['--fix-optional-new']);
process.stdin.writeln('foo({a:1}) {');
process.stdin.writeln(' new Bar();}');
await process.stdin.close();
expect(await process.stdout.next, 'foo({a: 1}) {');
expect(await process.stdout.next, ' Bar();');
expect(await process.stdout.next, '}');
await process.shouldExit(0);
});
test('errors with --fix and specific fix flag', () async {
var process =
await runFormatter(['--fix', '--fix-named-default-separator']);
await process.shouldExit(64);
});
});
group('with no paths', () {
test('errors on --overwrite', () async {
var process = await runFormatter(['--overwrite']);
await process.shouldExit(64);
});
test('exits with 65 on parse error', () async {
var process = await runFormatter();
process.stdin.writeln('herp derp i are a dart');
await process.stdin.close();
await process.shouldExit(65);
});
test('reads from stdin', () async {
var process = await runFormatter();
process.stdin.writeln(unformattedSource);
await process.stdin.close();
// No trailing newline at the end.
expect(await process.stdout.next, formattedOutput);
await process.shouldExit(0);
});
test('allows specifying stdin path name', () async {
var path = p.join('some', 'path.dart');
var process = await runFormatter(['--stdin-name=$path']);
process.stdin.writeln('herp');
await process.stdin.close();
expect(await process.stderr.next,
'Could not format because the source could not be parsed:');
expect(await process.stderr.next, '');
expect(await process.stderr.next, contains(path));
await process.stderr.cancel();
await process.shouldExit(65);
});
});
}