| // Copyright 2014 The Flutter 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 'package:file/file.dart'; |
| import 'package:file/memory.dart'; |
| import 'package:platform/platform.dart'; |
| import 'package:pub_semver/pub_semver.dart'; |
| import 'package:test/test.dart'; |
| |
| import '../../../packages/flutter_tools/test/src/fake_process_manager.dart'; |
| import '../create_api_docs.dart' as apidocs; |
| import '../dartdoc_checker.dart'; |
| |
| void main() { |
| group('FlutterInformation', () { |
| late FakeProcessManager fakeProcessManager; |
| late FakePlatform fakePlatform; |
| late MemoryFileSystem memoryFileSystem; |
| late apidocs.FlutterInformation flutterInformation; |
| |
| void setUpWithEnvironment(Map<String, String> environment) { |
| fakePlatform = FakePlatform(environment: environment); |
| flutterInformation = apidocs.FlutterInformation( |
| filesystem: memoryFileSystem, |
| processManager: fakeProcessManager, |
| platform: fakePlatform, |
| ); |
| apidocs.FlutterInformation.instance = flutterInformation; |
| } |
| |
| setUp(() { |
| fakeProcessManager = FakeProcessManager.empty(); |
| memoryFileSystem = MemoryFileSystem(); |
| setUpWithEnvironment(<String, String>{}); |
| }); |
| |
| test('getBranchName does not call git if env LUCI_BRANCH provided', () { |
| setUpWithEnvironment( |
| <String, String>{ |
| 'LUCI_BRANCH': branchName, |
| }, |
| ); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'rev-parse', 'HEAD'], |
| )); |
| expect( |
| apidocs.FlutterInformation.instance.getBranchName(), |
| branchName, |
| ); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| }); |
| |
| test('getBranchName calls git if env LUCI_BRANCH not provided', () { |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'rev-parse', 'HEAD'], |
| )); |
| |
| expect( |
| apidocs.FlutterInformation.instance.getBranchName(), |
| branchName, |
| ); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| }); |
| |
| test('getBranchName calls git if env LUCI_BRANCH is empty', () { |
| setUpWithEnvironment( |
| <String, String>{ |
| 'LUCI_BRANCH': '', |
| }, |
| ); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'rev-parse', 'HEAD'], |
| )); |
| |
| expect( |
| apidocs.FlutterInformation.instance.getBranchName(), |
| branchName, |
| ); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| }); |
| |
| test("runPubProcess doesn't use the pub binary", () { |
| final Platform platform = FakePlatform( |
| environment: <String, String>{ |
| 'FLUTTER_ROOT': '/flutter', |
| }, |
| ); |
| final ProcessManager processManager = FakeProcessManager.list( |
| <FakeCommand>[ |
| const FakeCommand( |
| command: <String>['/flutter/bin/flutter', 'pub', '--one', '--two'], |
| ), |
| ], |
| ); |
| apidocs.FlutterInformation.instance = |
| apidocs.FlutterInformation(platform: platform, processManager: processManager, filesystem: memoryFileSystem); |
| |
| apidocs.runPubProcess( |
| arguments: <String>['--one', '--two'], |
| processManager: processManager, |
| filesystem: memoryFileSystem, |
| ); |
| |
| expect(processManager, hasNoRemainingExpectations); |
| }); |
| |
| test('calls out to flutter if FLUTTER_VERSION is not set', () async { |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'rev-parse', 'HEAD'], |
| )); |
| final Map<String, dynamic> info = flutterInformation.getFlutterInformation(); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| expect(info['frameworkVersion'], equals(Version.parse('2.5.0'))); |
| }); |
| test("doesn't call out to flutter if FLUTTER_VERSION is set", () async { |
| setUpWithEnvironment(<String, String>{ |
| 'FLUTTER_VERSION': testVersionInfo, |
| }); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'rev-parse', 'HEAD'], |
| )); |
| final Map<String, dynamic> info = flutterInformation.getFlutterInformation(); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| expect(info['frameworkVersion'], equals(Version.parse('2.5.0'))); |
| }); |
| test('getFlutterRoot calls out to flutter if FLUTTER_ROOT is not set', () async { |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| )); |
| fakeProcessManager.addCommand(const FakeCommand( |
| command: <Pattern>['git', 'rev-parse', 'HEAD'], |
| )); |
| final Directory root = flutterInformation.getFlutterRoot(); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| expect(root.path, equals('/home/user/flutter')); |
| }); |
| test("getFlutterRoot doesn't call out to flutter if FLUTTER_ROOT is set", () async { |
| setUpWithEnvironment(<String, String>{'FLUTTER_ROOT': '/home/user/flutter'}); |
| final Directory root = flutterInformation.getFlutterRoot(); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| expect(root.path, equals('/home/user/flutter')); |
| }); |
| test('parses version properly', () async { |
| fakePlatform.environment['FLUTTER_VERSION'] = testVersionInfo; |
| fakeProcessManager.addCommands(<FakeCommand>[ |
| const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| ), |
| const FakeCommand( |
| command: <String>['git', 'rev-parse', 'HEAD'], |
| ), |
| ]); |
| final Map<String, dynamic> info = flutterInformation.getFlutterInformation(); |
| expect(info['frameworkVersion'], isNotNull); |
| expect(info['frameworkVersion'], equals(Version.parse('2.5.0'))); |
| expect(info['dartSdkVersion'], isNotNull); |
| expect(info['dartSdkVersion'], equals(Version.parse('2.14.0-360.0.dev'))); |
| }); |
| test('the engine realm is read from the engine.realm file', () async { |
| final Directory flutterHome = memoryFileSystem |
| .directory('/home') |
| .childDirectory('user') |
| .childDirectory('flutter') |
| .childDirectory('bin') |
| .childDirectory('internal'); |
| flutterHome.childFile('engine.realm') |
| ..createSync(recursive: true) |
| ..writeAsStringSync('realm'); |
| setUpWithEnvironment(<String, String>{'FLUTTER_ROOT': '/home/user/flutter'}); |
| fakeProcessManager.addCommands(<FakeCommand>[ |
| const FakeCommand( |
| command: <Pattern>['/home/user/flutter/bin/flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| ), |
| const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| ), |
| const FakeCommand( |
| command: <String>['git', 'rev-parse', 'HEAD'], |
| ), |
| ]); |
| final Map<String, dynamic> info = flutterInformation.getFlutterInformation(); |
| expect(fakeProcessManager, hasNoRemainingExpectations); |
| expect(info['engineRealm'], equals('realm')); |
| }); |
| }); |
| |
| group('DartDocGenerator', () { |
| late apidocs.DartdocGenerator generator; |
| late MemoryFileSystem fs; |
| late FakeProcessManager processManager; |
| late Directory publishRoot; |
| |
| setUp(() { |
| fs = MemoryFileSystem.test(); |
| publishRoot = fs.directory('/path/to/publish'); |
| processManager = FakeProcessManager.empty(); |
| generator = apidocs.DartdocGenerator( |
| packageRoot: fs.directory('/path/to/package'), |
| publishRoot: publishRoot, |
| docsRoot: fs.directory('/path/to/docs'), |
| filesystem: fs, |
| processManager: processManager, |
| ); |
| final Directory repoRoot = fs.directory('/flutter'); |
| repoRoot.childDirectory('packages').createSync(recursive: true); |
| apidocs.FlutterInformation.instance = apidocs.FlutterInformation( |
| filesystem: fs, |
| processManager: processManager, |
| platform: FakePlatform(environment: <String, String>{ |
| 'FLUTTER_ROOT': repoRoot.path, |
| }), |
| ); |
| }); |
| |
| test('.generateDartDoc() invokes dartdoc with the correct command line arguments', () async { |
| processManager.addCommands(<FakeCommand>[ |
| const FakeCommand(command: <String>['/flutter/bin/flutter', 'pub', 'get']), |
| const FakeCommand( |
| command: <String>['/flutter/bin/flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| ), |
| const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| ), |
| const FakeCommand( |
| command: <String>['git', 'rev-parse', 'HEAD'], |
| ), |
| const FakeCommand( |
| command: <String>['/flutter/bin/flutter', 'pub', 'global', 'list'], |
| ), |
| FakeCommand( |
| command: <Pattern>[ |
| '/flutter/bin/flutter', |
| 'pub', |
| 'global', |
| 'run', |
| '--enable-asserts', |
| 'dartdoc', |
| '--output', |
| '/path/to/publish/flutter', |
| '--allow-tools', |
| '--json', |
| '--validate-links', |
| '--link-to-source-excludes', |
| '/flutter/bin/cache', |
| '--link-to-source-root', |
| '/flutter', |
| '--link-to-source-uri-template', |
| 'https://github.com/flutter/flutter/blob/master/%f%#L%l%', |
| '--inject-html', |
| '--use-base-href', |
| '--header', |
| '/path/to/docs/styles.html', |
| '--header', |
| '/path/to/docs/analytics-header.html', |
| '--header', |
| '/path/to/docs/survey.html', |
| '--header', |
| '/path/to/docs/snippets.html', |
| '--header', |
| '/path/to/docs/opensearch.html', |
| '--footer', |
| '/path/to/docs/analytics-footer.html', |
| '--footer-text', |
| '/path/to/package/footer.html', |
| '--allow-warnings-in-packages', |
| // match package names |
| RegExp(r'^(\w+,)+(\w+)$'), |
| '--exclude-packages', |
| RegExp(r'^(\w+,)+(\w+)$'), |
| '--exclude', |
| // match dart package URIs |
| RegExp(r'^([\w\/:.]+,)+([\w\/:.]+)$'), |
| '--favicon', |
| '/path/to/docs/favicon.ico', |
| '--package-order', |
| 'flutter,Dart,${apidocs.kPlatformIntegrationPackageName},flutter_test,flutter_driver', |
| '--auto-include-dependencies', |
| ], |
| ), |
| ]); |
| |
| // This will throw while sanity checking generated files, which is tested independently |
| await expectLater( |
| () => generator.generateDartdoc(), |
| throwsA( |
| isA<Exception>().having( |
| (Exception e) => e.toString(), |
| 'message', |
| contains(RegExp(r'Missing .* which probably means the documentation failed to build correctly.')), |
| ), |
| ), |
| ); |
| |
| expect(processManager, hasNoRemainingExpectations); |
| }); |
| |
| test('sanity checks spot check generated files', () async { |
| processManager.addCommands(<FakeCommand>[ |
| const FakeCommand(command: <String>['/flutter/bin/flutter', 'pub', 'get']), |
| const FakeCommand( |
| command: <String>['/flutter/bin/flutter', '--version', '--machine'], |
| stdout: testVersionInfo, |
| ), |
| const FakeCommand( |
| command: <Pattern>['git', 'status', '-b', '--porcelain'], |
| stdout: '## $branchName', |
| ), |
| const FakeCommand( |
| command: <String>['git', 'rev-parse', 'HEAD'], |
| ), |
| const FakeCommand( |
| command: <String>['/flutter/bin/flutter', 'pub', 'global', 'list'], |
| ), |
| FakeCommand( |
| command: <Pattern>[ |
| '/flutter/bin/flutter', |
| 'pub', |
| 'global', |
| 'run', |
| '--enable-asserts', |
| 'dartdoc', |
| '--output', |
| '/path/to/publish/flutter', |
| '--allow-tools', |
| '--json', |
| '--validate-links', |
| '--link-to-source-excludes', |
| '/flutter/bin/cache', |
| '--link-to-source-root', |
| '/flutter', |
| '--link-to-source-uri-template', |
| 'https://github.com/flutter/flutter/blob/master/%f%#L%l%', |
| '--inject-html', |
| '--use-base-href', |
| '--header', |
| '/path/to/docs/styles.html', |
| '--header', |
| '/path/to/docs/analytics-header.html', |
| '--header', |
| '/path/to/docs/survey.html', |
| '--header', |
| '/path/to/docs/snippets.html', |
| '--header', |
| '/path/to/docs/opensearch.html', |
| '--footer', |
| '/path/to/docs/analytics-footer.html', |
| '--footer-text', |
| '/path/to/package/footer.html', |
| '--allow-warnings-in-packages', |
| // match package names |
| RegExp(r'^(\w+,)+(\w+)$'), |
| '--exclude-packages', |
| RegExp(r'^(\w+,)+(\w+)$'), |
| '--exclude', |
| // match dart package URIs |
| RegExp(r'^([\w\/:.]+,)+([\w\/:.]+)$'), |
| '--favicon', |
| '/path/to/docs/favicon.ico', |
| '--package-order', |
| 'flutter,Dart,${apidocs.kPlatformIntegrationPackageName},flutter_test,flutter_driver', |
| '--auto-include-dependencies', |
| ], |
| onRun: () { |
| for (final File canary in generator.canaries) { |
| canary.createSync(recursive: true); |
| } |
| for (final String path in dartdocDirectiveCanaryFiles) { |
| publishRoot.childDirectory('flutter').childFile(path).createSync(recursive: true); |
| } |
| for (final String path in dartdocDirectiveCanaryLibraries) { |
| publishRoot.childDirectory('flutter').childDirectory(path).createSync(recursive: true); |
| } |
| publishRoot.childDirectory('flutter').childFile('index.html').createSync(); |
| |
| final Directory widgetsDir = publishRoot |
| .childDirectory('flutter') |
| .childDirectory('widgets') |
| ..createSync(recursive: true); |
| widgetsDir.childFile('showGeneralDialog.html').writeAsStringSync(''' |
| <pre id="longSnippet1"> |
| <code class="language-dart"> |
| import 'package:flutter/material.dart'; |
| </code> |
| </pre> |
| ''', |
| ); |
| expect(publishRoot.childDirectory('flutter').existsSync(), isTrue); |
| (widgetsDir |
| .childDirectory('ModalRoute') |
| ..createSync(recursive: true)) |
| .childFile('barrierColor.html') |
| .writeAsStringSync(''' |
| <pre id="sample-code"> |
| <code class="language-dart"> |
| class FooClass { |
| Color get barrierColor => FooColor(); |
| } |
| </code> |
| </pre> |
| '''); |
| const String queryParams = 'split=1&run=true&sample_id=widgets.Listener.123&sample_channel=master&channel=master'; |
| widgetsDir.childFile('Listener-class.html').writeAsStringSync(''' |
| <iframe class="snippet-dartpad" src="https://dartpad.dev/embed-flutter.html?$queryParams"> |
| </iframe> |
| '''); |
| } |
| ), |
| ]); |
| |
| await generator.generateDartdoc(); |
| }); |
| }); |
| } |
| |
| const String branchName = 'stable'; |
| const String testVersionInfo = ''' |
| { |
| "frameworkVersion": "2.5.0", |
| "channel": "$branchName", |
| "repositoryUrl": "git@github.com:flutter/flutter.git", |
| "frameworkRevision": "0000000000000000000000000000000000000000", |
| "frameworkCommitDate": "2021-07-28 13:03:40 -0700", |
| "engineRevision": "0000000000000000000000000000000000000001", |
| "dartSdkVersion": "2.14.0 (build 2.14.0-360.0.dev)", |
| "flutterRoot": "/home/user/flutter" |
| } |
| '''; |