analyze_test.dart 11.7 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:io';

import 'package:path/path.dart' as path;

import '../analyze.dart';
10
import '../utils.dart';
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
import 'common.dart';

typedef AsyncVoidCallback = Future<void> Function();

Future<String> capture(AsyncVoidCallback callback, { int exitCode = 0 }) async {
  final StringBuffer buffer = StringBuffer();
  final PrintCallback oldPrint = print;
  try {
    print = (Object line) {
      buffer.writeln(line);
    };
    try {
      await callback();
      expect(exitCode, 0);
    } on ExitException catch (error) {
      expect(error.exitCode, exitCode);
    }
  } finally {
    print = oldPrint;
  }
31 32 33 34 35 36
  if (stdout.supportsAnsiEscapes) {
    // Remove ANSI escapes when this test is running on a terminal.
    return buffer.toString().replaceAll(RegExp(r'(\x9B|\x1B\[)[0-?]{1,3}[ -/]*[@-~]'), '');
  } else {
    return buffer.toString();
  }
37 38 39
}

void main() {
40
  final String testRootPath = path.join('test', 'analyze-test-input', 'root');
41 42
  final String dartName = Platform.isWindows ? 'dart.exe' : 'dart';
  final String dartPath = path.canonicalize(path.join('..', '..', 'bin', 'cache', 'dart-sdk', 'bin', dartName));
43

44
  test('analyze.dart - verifyDeprecations', () async {
45
    final String result = await capture(() => verifyDeprecations(testRootPath, minimumMatches: 2), exitCode: 1);
46 47 48 49 50 51 52 53 54 55
    final String lines = <String>[
        'test/analyze-test-input/root/packages/foo/deprecation.dart:12: Deprecation notice does not match required pattern.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:18: Deprecation notice should be a grammatically correct sentence and start with a capital letter; see style guide: STYLE_GUIDE_URL',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:25: Deprecation notice should be a grammatically correct sentence and end with a period.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:29: Deprecation notice does not match required pattern.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:32: Deprecation notice does not match required pattern.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:37: Deprecation notice does not match required pattern.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:41: Deprecation notice does not match required pattern.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:48: End of deprecation notice does not match required pattern.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:51: Unexpected deprecation notice indent.',
56 57
        'test/analyze-test-input/root/packages/foo/deprecation.dart:70: Deprecation notice does not accurately indicate a beta branch version number; please see RELEASES_URL to find the latest beta build version number.',
        'test/analyze-test-input/root/packages/foo/deprecation.dart:76: Deprecation notice does not accurately indicate a beta branch version number; please see RELEASES_URL to find the latest beta build version number.',
58 59 60 61 62 63 64 65 66
        'test/analyze-test-input/root/packages/foo/deprecation.dart:99: Deprecation notice does not match required pattern. You might have used double quotes (") for the string instead of single quotes (\').',
      ]
      .map((String line) {
        return line
          .replaceAll('/', Platform.isWindows ? r'\' : '/')
          .replaceAll('STYLE_GUIDE_URL', 'https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo')
          .replaceAll('RELEASES_URL', 'https://flutter.dev/docs/development/tools/sdk/releases');
      })
      .join('\n');
67 68
    expect(result,
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
69
      '$lines\n'
70 71 72 73 74
      'See: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes\n'
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
    );
  });

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
  test('analyze.dart - verifyGoldenTags', () async {
    final String result = await capture(() => verifyGoldenTags(testRootPath, minimumMatches: 6), exitCode: 1);
    const String noTag = 'Files containing golden tests must be '
        'tagged using `@Tags(...)` at the top of the file before import statements.';
    const String missingTag = 'Files containing golden tests must be '
        "tagged with 'reduced-test-set'.";
    String lines = <String>[
        'test/analyze-test-input/root/packages/foo/golden_missing_tag.dart: $missingTag',
        'test/analyze-test-input/root/packages/foo/golden_no_tag.dart: $noTag',
      ]
      .map((String line) {
        return line
          .replaceAll('/', Platform.isWindows ? r'\' : '/');
      })
      .join('\n');

    try {
      expect(
        result,
        '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
        '$lines\n'
        'See: https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter\n'
        '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
      );
    } catch (_) {
      // This list of files may come up in one order or the other.
      lines = <String>[
        'test/analyze-test-input/root/packages/foo/golden_no_tag.dart: $noTag',
        'test/analyze-test-input/root/packages/foo/golden_missing_tag.dart: $missingTag',
      ]
      .map((String line) {
        return line
          .replaceAll('/', Platform.isWindows ? r'\' : '/');
      })
      .join('\n');
      expect(
        result,
        '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
        '$lines\n'
        'See: https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter\n'
        '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
      );
    }
  });

120
  test('analyze.dart - verifyNoMissingLicense', () async {
121
    final String result = await capture(() => verifyNoMissingLicense(testRootPath, checkMinimums: false), exitCode: 1);
122
    final String file = 'test/analyze-test-input/root/packages/foo/foo.dart'
123
      .replaceAll('/', Platform.isWindows ? r'\' : '/');
Ian Hickson's avatar
Ian Hickson committed
124 125
    expect(result,
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
126 127
      'The following file does not have the right license header for dart files:\n'
      '  $file\n'
Ian Hickson's avatar
Ian Hickson committed
128 129 130 131 132
      'The expected license header is:\n'
      '// Copyright 2014 The Flutter Authors. All rights reserved.\n'
      '// Use of this source code is governed by a BSD-style license that can be\n'
      '// found in the LICENSE file.\n'
      '...followed by a blank line.\n'
133
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
134 135 136
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
      'License check failed.\n'
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
Ian Hickson's avatar
Ian Hickson committed
137
    );
138
  });
139 140 141

  test('analyze.dart - verifyNoTrailingSpaces', () async {
    final String result = await capture(() => verifyNoTrailingSpaces(testRootPath, minimumMatches: 2), exitCode: 1);
142 143 144 145 146 147
    final String lines = <String>[
        'test/analyze-test-input/root/packages/foo/spaces.txt:5: trailing U+0020 space character',
        'test/analyze-test-input/root/packages/foo/spaces.txt:9: trailing blank line',
      ]
      .map((String line) => line.replaceAll('/', Platform.isWindows ? r'\' : '/'))
      .join('\n');
148 149
    expect(result,
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
150
      '$lines\n'
151 152 153 154 155 156 157
      '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
    );
  });

  test('analyze.dart - verifyNoBinaries - positive', () async {
    final String result = await capture(() => verifyNoBinaries(
      testRootPath,
158
      legacyBinaries: <Hash256>{const Hash256(0x39A050CD69434936, 0, 0, 0)},
159 160
    ), exitCode: Platform.isWindows ? 0 : 1);
    if (!Platform.isWindows) {
161
      expect(result,
162 163 164 165 166 167 168
        '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
        'test/analyze-test-input/root/packages/foo/serviceaccount.enc:0: file is not valid UTF-8\n'
        'All files in this repository must be UTF-8. In particular, images and other binaries\n'
        'must not be checked into this repository. This is because we are very sensitive to the\n'
        'size of the repository as it is distributed to all our developers. If you have a binary\n'
        'to which you need access, you should consider how to fetch it from another repository;\n'
        'for example, the "assets-for-api-docs" repository is used for images in API docs.\n'
169 170
        'To add assets to flutter_tools templates, see the instructions in the wiki:\n'
        'https://github.com/flutter/flutter/wiki/Managing-template-image-assets\n'
171
        '━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'
172
      );
173 174 175
    }
  });

176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
  test('analyze.dart - verifyInternationalizations - comparison fails', () async {
    final String result = await capture(() => verifyInternationalizations(testRootPath, dartPath), exitCode: 1);
    final String genLocalizationsScript = path.join('dev', 'tools', 'localization', 'bin', 'gen_localizations.dart');
    expect(result,
        contains('$dartName $genLocalizationsScript --cupertino'));
    expect(result,
        contains('$dartName $genLocalizationsScript --material'));
    final String generatedFile = path.join(testRootPath, 'packages', 'flutter_localizations',
        'lib', 'src', 'l10n', 'generated_material_localizations.dart');
    expect(result,
        contains('The contents of $generatedFile are different from that produced by gen_localizations.'));
    expect(result,
        contains(r'Did you forget to run gen_localizations.dart after updating a .arb file?'));
  });

191 192 193
  test('analyze.dart - verifyNoBinaries - negative', () async {
    await capture(() => verifyNoBinaries(
      testRootPath,
194
      legacyBinaries: <Hash256>{
195 196
        const Hash256(0xA8100AE6AA1940D0, 0xB663BB31CD466142, 0xEBBDBD5187131B92, 0xD93818987832EB89), // sha256("\xff")
        const Hash256(0x155644D3F13D98BF, 0, 0, 0),
197
      },
198
    ));
199
  });
200 201 202 203 204 205 206 207 208 209

  test('analyze.dart - verifyNullInitializedDebugExpensiveFields', () async {
    final String result = await capture(() => verifyNullInitializedDebugExpensiveFields(
      testRootPath,
      minimumMatches: 1,
    ), exitCode: 1);

    expect(result, contains('L15'));
    expect(result, isNot(contains('L12')));
  });
210
}