// 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 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({}); }); test('getBranchName does not call git if env LUCI_BRANCH provided', () { setUpWithEnvironment( { 'LUCI_BRANCH': branchName, }, ); fakeProcessManager.addCommand(const FakeCommand( command: ['flutter', '--version', '--machine'], stdout: testVersionInfo, )); fakeProcessManager.addCommand(const FakeCommand( command: ['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: ['flutter', '--version', '--machine'], stdout: testVersionInfo, )); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', )); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], )); expect( apidocs.FlutterInformation.instance.getBranchName(), branchName, ); expect(fakeProcessManager, hasNoRemainingExpectations); }); test('getBranchName calls git if env LUCI_BRANCH is empty', () { setUpWithEnvironment( { 'LUCI_BRANCH': '', }, ); fakeProcessManager.addCommand(const FakeCommand( command: ['flutter', '--version', '--machine'], stdout: testVersionInfo, )); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', )); fakeProcessManager.addCommand(const FakeCommand( command: ['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: { 'FLUTTER_ROOT': '/flutter', }, ); final ProcessManager processManager = FakeProcessManager.list( [ const FakeCommand( command: ['/flutter/bin/flutter', 'pub', '--one', '--two'], ), ], ); apidocs.FlutterInformation.instance = apidocs.FlutterInformation(platform: platform, processManager: processManager, filesystem: memoryFileSystem); apidocs.runPubProcess( arguments: ['--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: ['flutter', '--version', '--machine'], stdout: testVersionInfo, )); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', )); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], )); final Map 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({ 'FLUTTER_VERSION': testVersionInfo, }); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', )); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], )); final Map 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: ['flutter', '--version', '--machine'], stdout: testVersionInfo, )); fakeProcessManager.addCommand(const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', )); fakeProcessManager.addCommand(const FakeCommand( command: ['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({'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([ const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', ), const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], ), ]); final Map 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({'FLUTTER_ROOT': '/home/user/flutter'}); fakeProcessManager.addCommands([ const FakeCommand( command: ['/home/user/flutter/bin/flutter', '--version', '--machine'], stdout: testVersionInfo, ), const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', ), const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], ), ]); final Map info = flutterInformation.getFlutterInformation(); expect(fakeProcessManager, hasNoRemainingExpectations); expect(info['engineRealm'], equals('realm')); }); }); group('Configurator', () { late MemoryFileSystem fs; late FakeProcessManager fakeProcessManager; late Directory publishRoot; late Directory packageRoot; late Directory docsRoot; late File searchTemplate; late apidocs.Configurator configurator; late FakePlatform fakePlatform; late apidocs.FlutterInformation flutterInformation; void setUpWithEnvironment(Map environment) { fakePlatform = FakePlatform(environment: environment); flutterInformation = apidocs.FlutterInformation( filesystem: fs, processManager: fakeProcessManager, platform: fakePlatform, ); apidocs.FlutterInformation.instance = flutterInformation; } setUp(() { fs = MemoryFileSystem.test(); publishRoot = fs.directory('/path/to/publish'); packageRoot = fs.directory('/path/to/package'); docsRoot = fs.directory('/path/to/docs'); searchTemplate = docsRoot.childDirectory('lib').childFile('opensearch.xml'); fs.directory('/home/user/flutter/packages').createSync(recursive: true); fakeProcessManager = FakeProcessManager.empty(); setUpWithEnvironment({}); publishRoot.createSync(recursive: true); packageRoot.createSync(recursive: true); docsRoot.createSync(recursive: true); final List files = [ 'README.md', 'analysis_options.yaml', 'dartdoc_options.yaml', searchTemplate.path, publishRoot.childFile('opensearch.xml').path, ]; for (final String file in files) { docsRoot.childFile(file).createSync(recursive: true); } searchTemplate.writeAsStringSync('{SITE_URL}'); configurator = apidocs.Configurator( docsRoot: docsRoot, packageRoot: packageRoot, publishRoot: publishRoot, filesystem: fs, processManager: fakeProcessManager, platform: fakePlatform, ); fakeProcessManager.addCommands([ const FakeCommand( command: ['flutter', '--version', '--machine'], stdout: testVersionInfo, ), const FakeCommand( command: ['git', 'status', '-b', '--porcelain'], stdout: '## $branchName', ), const FakeCommand( command: ['git', 'rev-parse', 'HEAD'], ), const FakeCommand( command: ['/flutter/bin/flutter', 'pub', 'global', 'list'], ), FakeCommand( command: [ '/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/main/%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', ], ), ]); }); test('.generateConfiguration generates pubspec.yaml', () async { configurator.generateConfiguration(); expect(packageRoot.childFile('pubspec.yaml').existsSync(), isTrue); expect(packageRoot.childFile('pubspec.yaml').readAsStringSync(), contains('flutter_gpu:')); expect(packageRoot.childFile('pubspec.yaml').readAsStringSync(), contains('dependency_overrides:')); expect(packageRoot.childFile('pubspec.yaml').readAsStringSync(), contains('platform_integration:')); }); test('.generateConfiguration generates fake lib', () async { configurator.generateConfiguration(); expect(packageRoot.childDirectory('lib').existsSync(), isTrue); expect(packageRoot.childDirectory('lib').childFile('temp_doc.dart').existsSync(), isTrue); expect(packageRoot.childDirectory('lib').childFile('temp_doc.dart').readAsStringSync(), contains('library temp_doc;')); expect(packageRoot.childDirectory('lib').childFile('temp_doc.dart').readAsStringSync(), contains("import 'package:flutter_gpu/gpu.dart';")); }); test('.generateConfiguration generates page footer', () async { configurator.generateConfiguration(); expect(packageRoot.childFile('footer.html').existsSync(), isTrue); expect(packageRoot.childFile('footer.html').readAsStringSync(), contains('