// 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_testing/file_testing.dart'; import 'package:flutter_tools/src/base/io.dart'; import '../src/common.dart'; import 'test_utils.dart'; // This test file does not use [getLocalEngineArguments] because it requires // multiple specific artifact output types. const String apkDebugMessage = 'A summary of your APK analysis can be found at: '; const String iosDebugMessage = 'A summary of your iOS bundle analysis can be found at: '; const String macOSDebugMessage = 'A summary of your macOS bundle analysis can be found at: '; const String runDevToolsMessage = 'flutter pub global activate devtools; flutter pub global run devtools '; void main() { testWithoutContext('--analyze-size flag produces expected output on hello_world for Android', () async { final String workingDirectory = fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world'); final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final ProcessResult result = await processManager.run(<String>[ flutterBin, 'build', 'apk', '--analyze-size', '--target-platform=android-arm64' ], workingDirectory: workingDirectory); printOnFailure('Output of flutter build apk:'); printOnFailure(result.stdout.toString()); printOnFailure(result.stderr.toString()); expect(result.stdout.toString(), contains('app-release.apk (total compressed)')); final String line = result.stdout.toString() .split('\n') .firstWhere((String line) => line.contains(apkDebugMessage)); final String outputFilePath = line.split(apkDebugMessage).last.trim(); expect(fileSystem.file(fileSystem.path.join(workingDirectory, outputFilePath)), exists); expect(outputFilePath, contains('.flutter-devtools')); final String devToolsCommand = result.stdout.toString() .split('\n') .firstWhere((String line) => line.contains(runDevToolsMessage)); final String commandArguments = devToolsCommand.split(runDevToolsMessage).last.trim(); final String relativeAppSizePath = outputFilePath.split('.flutter-devtools/').last.trim(); expect(commandArguments.contains('--appSizeBase=$relativeAppSizePath'), isTrue); expect(result.exitCode, 0); }); testWithoutContext('--analyze-size flag produces expected output on hello_world for iOS', () async { final String workingDirectory = fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world'); final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_size_test.'); final Directory codeSizeDir = tempDir.childDirectory('code size dir')..createSync(); final ProcessResult result = await processManager.run(<String>[ flutterBin, 'build', 'ios', '--analyze-size', '--code-size-directory=${codeSizeDir.path}', '--no-codesign', ], workingDirectory: workingDirectory); printOnFailure('Output of flutter build ios:'); printOnFailure(result.stdout.toString()); printOnFailure(result.stderr.toString()); expect(result.stdout.toString(), contains('Dart AOT symbols accounted decompressed size')); final String line = result.stdout.toString() .split('\n') .firstWhere((String line) => line.contains(iosDebugMessage)); final String outputFilePath = line.split(iosDebugMessage).last.trim(); expect(fileSystem.file(fileSystem.path.join(workingDirectory, outputFilePath)), exists); final String devToolsCommand = result.stdout.toString() .split('\n') .firstWhere((String line) => line.contains(runDevToolsMessage)); final String commandArguments = devToolsCommand.split(runDevToolsMessage).last.trim(); final String relativeAppSizePath = outputFilePath.split('.flutter-devtools/').last.trim(); expect(commandArguments.contains('--appSizeBase=$relativeAppSizePath'), isTrue); expect(codeSizeDir.existsSync(), true); expect(result.exitCode, 0); tempDir.deleteSync(recursive: true); }, skip: !platform.isMacOS); // [intended] iOS can only be built on macos. testWithoutContext('--analyze-size flag produces expected output on hello_world for macOS', () async { final String workingDirectory = fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world'); final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_size_test.'); final Directory codeSizeDir = tempDir.childDirectory('code size dir')..createSync(); final ProcessResult configResult = await processManager.run(<String>[ flutterBin, 'config', '--enable-macos-desktop', ], workingDirectory: workingDirectory); printOnFailure('Output of flutter config:'); printOnFailure(configResult.stdout.toString()); printOnFailure(configResult.stderr.toString()); final ProcessResult result = await processManager.run(<String>[ flutterBin, 'build', 'macos', '--analyze-size', '--code-size-directory=${codeSizeDir.path}', ], workingDirectory: workingDirectory); printOnFailure('Output of flutter build macos:'); printOnFailure(result.stdout.toString()); printOnFailure(result.stderr.toString()); expect(result.stdout.toString(), contains('Dart AOT symbols accounted decompressed size')); final String line = result.stdout.toString() .split('\n') .firstWhere((String line) => line.contains(macOSDebugMessage)); final String outputFilePath = line.split(macOSDebugMessage).last.trim(); expect(fileSystem.file(fileSystem.path.join(workingDirectory, outputFilePath)), exists); final String devToolsCommand = result.stdout.toString() .split('\n') .firstWhere((String line) => line.contains(runDevToolsMessage)); final String commandArguments = devToolsCommand.split(runDevToolsMessage).last.trim(); final String relativeAppSizePath = outputFilePath.split('.flutter-devtools/').last.trim(); expect(commandArguments.contains('--appSizeBase=$relativeAppSizePath'), isTrue); expect(codeSizeDir.existsSync(), true); expect(result.exitCode, 0); tempDir.deleteSync(recursive: true); }, skip: !platform.isMacOS); // [intended] this is a macos only test. testWithoutContext('--analyze-size is only supported in release mode', () async { final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final ProcessResult result = await processManager.run(<String>[ flutterBin, 'build', 'apk', '--analyze-size', '--target-platform=android-arm64', '--debug', ], workingDirectory: fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world')); printOnFailure('Output of flutter build apk:'); printOnFailure(result.stdout.toString()); printOnFailure(result.stderr.toString()); expect(result.stderr.toString(), contains('"--analyze-size" can only be used on release builds')); expect(result.exitCode, 1); }); testWithoutContext('--analyze-size is not supported in combination with --split-debug-info', () async { final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final ProcessResult result = await processManager.run(<String>[ flutterBin, 'build', 'apk', '--analyze-size', '--target-platform=android-arm64', '--split-debug-info=infos' ], workingDirectory: fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world')); expect(result.stderr.toString(), contains('"--analyze-size" cannot be combined with "--split-debug-info"')); expect(result.exitCode, 1); }); testWithoutContext('--analyze-size allows overriding the directory for code size files', () async { final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_size_test.'); final ProcessResult result = await processManager.run(<String>[ flutterBin, 'build', 'apk', '--analyze-size', '--code-size-directory=${tempDir.path}', '--target-platform=android-arm64', '--release', ], workingDirectory: fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world')); expect(result.exitCode, 0); expect(tempDir.existsSync(), true); expect(tempDir.childFile('snapshot.arm64-v8a.json').existsSync(), true); expect(tempDir.childFile('trace.arm64-v8a.json').existsSync(), true); tempDir.deleteSync(recursive: true); }); }