Unverified Commit 8d3bc184 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Migrate analyze_size to null safety (#81002)

parent b8833afc
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:archive/archive.dart'; import 'package:archive/archive.dart';
import 'package:archive/archive_io.dart'; import 'package:archive/archive_io.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
...@@ -11,6 +9,7 @@ import 'package:vm_snapshot_analysis/treemap.dart'; ...@@ -11,6 +9,7 @@ import 'package:vm_snapshot_analysis/treemap.dart';
import '../convert.dart'; import '../convert.dart';
import '../reporting/reporting.dart'; import '../reporting/reporting.dart';
import 'common.dart';
import 'file_system.dart'; import 'file_system.dart';
import 'logger.dart'; import 'logger.dart';
import 'terminal.dart'; import 'terminal.dart';
...@@ -18,10 +17,9 @@ import 'terminal.dart'; ...@@ -18,10 +17,9 @@ import 'terminal.dart';
/// A class to analyze APK and AOT snapshot and generate a breakdown of the data. /// A class to analyze APK and AOT snapshot and generate a breakdown of the data.
class SizeAnalyzer { class SizeAnalyzer {
SizeAnalyzer({ SizeAnalyzer({
@required FileSystem fileSystem, required FileSystem fileSystem,
@required Logger logger, required Logger logger,
// TODO(jonahwilliams): migrate to required once this has rolled into google3. required Usage flutterUsage,
Usage flutterUsage,
Pattern appFilenamePattern = 'libapp.so', Pattern appFilenamePattern = 'libapp.so',
}) : _flutterUsage = flutterUsage, }) : _flutterUsage = flutterUsage,
_fileSystem = fileSystem, _fileSystem = fileSystem,
...@@ -32,7 +30,7 @@ class SizeAnalyzer { ...@@ -32,7 +30,7 @@ class SizeAnalyzer {
final Logger _logger; final Logger _logger;
final Pattern _appFilenamePattern; final Pattern _appFilenamePattern;
final Usage _flutterUsage; final Usage _flutterUsage;
String _appFilename; String? _appFilename;
static const String aotSnapshotFileName = 'aot-snapshot.json'; static const String aotSnapshotFileName = 'aot-snapshot.json';
static const int tableWidth = 80; static const int tableWidth = 80;
...@@ -40,12 +38,12 @@ class SizeAnalyzer { ...@@ -40,12 +38,12 @@ class SizeAnalyzer {
static const int _kZipSizeMaxDepth = 1; static const int _kZipSizeMaxDepth = 1;
/// Analyze the [aotSnapshot] in an uncompressed output directory. /// Analyze the [aotSnapshot] in an uncompressed output directory.
Future<Map<String, dynamic>> analyzeAotSnapshot({ Future<Map<String, Object?>> analyzeAotSnapshot({
@required Directory outputDirectory, required Directory outputDirectory,
@required File aotSnapshot, required File aotSnapshot,
@required File precompilerTrace, required File precompilerTrace,
@required String type, required String type,
String excludePath, String? excludePath,
}) async { }) async {
_logger.printStatus('▒' * tableWidth); _logger.printStatus('▒' * tableWidth);
_logger.printStatus('━' * tableWidth); _logger.printStatus('━' * tableWidth);
...@@ -56,10 +54,12 @@ class SizeAnalyzer { ...@@ -56,10 +54,12 @@ class SizeAnalyzer {
); );
// Convert an AOT snapshot file into a map. // Convert an AOT snapshot file into a map.
final Map<String, dynamic> processedAotSnapshotJson = treemapFromJson( final Object? decodedAotSnapshot = json.decode(aotSnapshot.readAsStringSync());
json.decode(aotSnapshot.readAsStringSync()), if (decodedAotSnapshot == null) {
); throwToolExit('AOT snapshot is invalid for analysis');
final _SymbolNode aotSnapshotJsonRoot = _parseAotSnapshot(processedAotSnapshotJson); }
final Map<String, Object?> processedAotSnapshotJson = treemapFromJson(decodedAotSnapshot);
final _SymbolNode? aotSnapshotJsonRoot = _parseAotSnapshot(processedAotSnapshotJson);
for (final _SymbolNode firstLevelPath in aotAnalysisJson.children) { for (final _SymbolNode firstLevelPath in aotAnalysisJson.children) {
_printEntitySize( _printEntitySize(
...@@ -68,14 +68,14 @@ class SizeAnalyzer { ...@@ -68,14 +68,14 @@ class SizeAnalyzer {
level: 1, level: 1,
); );
// Print the expansion of lib directory to show more info for `appFilename`. // Print the expansion of lib directory to show more info for `appFilename`.
if (firstLevelPath.name == _fileSystem.path.basename(outputDirectory.path)) { if (firstLevelPath.name == _fileSystem.path.basename(outputDirectory.path) && aotSnapshotJsonRoot != null) {
_printLibChildrenPaths(firstLevelPath, '', aotSnapshotJsonRoot, _kAotSizeMaxDepth, 0); _printLibChildrenPaths(firstLevelPath, '', aotSnapshotJsonRoot, _kAotSizeMaxDepth, 0);
} }
} }
_logger.printStatus('▒' * tableWidth); _logger.printStatus('▒' * tableWidth);
Map<String, dynamic> apkAnalysisJson = aotAnalysisJson.toJson(); Map<String, Object?> apkAnalysisJson = aotAnalysisJson.toJson();
apkAnalysisJson['type'] = type; // one of apk, aab, ios, macos, windows, or linux. apkAnalysisJson['type'] = type; // one of apk, aab, ios, macos, windows, or linux.
...@@ -83,7 +83,7 @@ class SizeAnalyzer { ...@@ -83,7 +83,7 @@ class SizeAnalyzer {
apkAnalysisJson: apkAnalysisJson, apkAnalysisJson: apkAnalysisJson,
path: _locatedAotFilePath, path: _locatedAotFilePath,
aotSnapshotJson: processedAotSnapshotJson, aotSnapshotJson: processedAotSnapshotJson,
precompilerTrace: json.decode(precompilerTrace.readAsStringSync()) as Map<String, Object>, precompilerTrace: json.decode(precompilerTrace.readAsStringSync()) as Map<String, Object?>? ?? <String, Object?>{},
); );
assert(_appFilename != null); assert(_appFilename != null);
...@@ -97,11 +97,11 @@ class SizeAnalyzer { ...@@ -97,11 +97,11 @@ class SizeAnalyzer {
/// ///
/// [kind] must be one of 'apk' or 'aab'. /// [kind] must be one of 'apk' or 'aab'.
/// The [aotSnapshot] can be either instruction sizes snapshot or a v8 snapshot. /// The [aotSnapshot] can be either instruction sizes snapshot or a v8 snapshot.
Future<Map<String, dynamic>> analyzeZipSizeAndAotSnapshot({ Future<Map<String, Object?>> analyzeZipSizeAndAotSnapshot({
@required File zipFile, required File zipFile,
@required File aotSnapshot, required File aotSnapshot,
@required File precompilerTrace, required File precompilerTrace,
@required String kind, required String kind,
}) async { }) async {
assert(kind == 'apk' || kind == 'aab'); assert(kind == 'apk' || kind == 'aab');
_logger.printStatus('▒' * tableWidth); _logger.printStatus('▒' * tableWidth);
...@@ -116,16 +116,20 @@ class SizeAnalyzer { ...@@ -116,16 +116,20 @@ class SizeAnalyzer {
final _SymbolNode apkAnalysisRoot = _parseUnzipFile(zipFile); final _SymbolNode apkAnalysisRoot = _parseUnzipFile(zipFile);
// Convert an AOT snapshot file into a map. // Convert an AOT snapshot file into a map.
final Map<String, dynamic> processedAotSnapshotJson = treemapFromJson( final Object? decodedAotSnapshot = json.decode(aotSnapshot.readAsStringSync());
json.decode(aotSnapshot.readAsStringSync()), if (decodedAotSnapshot == null) {
); throwToolExit('AOT snapshot is invalid for analysis');
final _SymbolNode aotSnapshotJsonRoot = _parseAotSnapshot(processedAotSnapshotJson); }
final Map<String, Object?> processedAotSnapshotJson = treemapFromJson(decodedAotSnapshot);
final _SymbolNode? aotSnapshotJsonRoot = _parseAotSnapshot(processedAotSnapshotJson);
if (aotSnapshotJsonRoot != null) {
for (final _SymbolNode firstLevelPath in apkAnalysisRoot.children) { for (final _SymbolNode firstLevelPath in apkAnalysisRoot.children) {
_printLibChildrenPaths(firstLevelPath, '', aotSnapshotJsonRoot, _kZipSizeMaxDepth, 0); _printLibChildrenPaths(firstLevelPath, '', aotSnapshotJsonRoot, _kZipSizeMaxDepth, 0);
} }
}
_logger.printStatus('▒' * tableWidth); _logger.printStatus('▒' * tableWidth);
Map<String, dynamic> apkAnalysisJson = apkAnalysisRoot.toJson(); Map<String, Object?> apkAnalysisJson = apkAnalysisRoot.toJson();
apkAnalysisJson['type'] = kind; apkAnalysisJson['type'] = kind;
...@@ -134,7 +138,7 @@ class SizeAnalyzer { ...@@ -134,7 +138,7 @@ class SizeAnalyzer {
apkAnalysisJson: apkAnalysisJson, apkAnalysisJson: apkAnalysisJson,
path: _locatedAotFilePath, path: _locatedAotFilePath,
aotSnapshotJson: processedAotSnapshotJson, aotSnapshotJson: processedAotSnapshotJson,
precompilerTrace: json.decode(precompilerTrace.readAsStringSync()) as Map<String, Object>, precompilerTrace: json.decode(precompilerTrace.readAsStringSync()) as Map<String, Object?>? ?? <String, Object?>{},
); );
CodeSizeEvent(kind, flutterUsage: _flutterUsage).send(); CodeSizeEvent(kind, flutterUsage: _flutterUsage).send();
return apkAnalysisJson; return apkAnalysisJson;
...@@ -145,12 +149,15 @@ class SizeAnalyzer { ...@@ -145,12 +149,15 @@ class SizeAnalyzer {
final Map<List<String>, int> pathsToSize = <List<String>, int>{}; final Map<List<String>, int> pathsToSize = <List<String>, int>{};
for (final ArchiveFile archiveFile in archive.files) { for (final ArchiveFile archiveFile in archive.files) {
pathsToSize[_fileSystem.path.split(archiveFile.name)] = archiveFile.rawContent.length; final InputStream? rawContent = archiveFile.rawContent;
if (rawContent != null) {
pathsToSize[_fileSystem.path.split(archiveFile.name)] = rawContent.length;
}
} }
return _buildSymbolTree(pathsToSize); return _buildSymbolTree(pathsToSize);
} }
_SymbolNode _parseDirectory(Directory directory, String relativeTo, String excludePath) { _SymbolNode _parseDirectory(Directory directory, String relativeTo, String? excludePath) {
final Map<List<String>, int> pathsToSize = <List<String>, int>{}; final Map<List<String>, int> pathsToSize = <List<String>, int>{};
for (final File file in directory.listSync(recursive: true).whereType<File>()) { for (final File file in directory.listSync(recursive: true).whereType<File>()) {
if (excludePath != null && file.uri.pathSegments.contains(excludePath)) { if (excludePath != null && file.uri.pathSegments.contains(excludePath)) {
...@@ -163,9 +170,9 @@ class SizeAnalyzer { ...@@ -163,9 +170,9 @@ class SizeAnalyzer {
return _buildSymbolTree(pathsToSize); return _buildSymbolTree(pathsToSize);
} }
List<String> _locatedAotFilePath; List<String> _locatedAotFilePath = <String>[];
List<String> _buildNodeName(_SymbolNode start, _SymbolNode parent) { List<String> _buildNodeName(_SymbolNode start, _SymbolNode? parent) {
final List<String> results = <String>[start.name]; final List<String> results = <String>[start.name];
while (parent != null && parent.name != 'Root') { while (parent != null && parent.name != 'Root') {
results.insert(0, parent.name); results.insert(0, parent.name);
...@@ -180,7 +187,7 @@ class SizeAnalyzer { ...@@ -180,7 +187,7 @@ class SizeAnalyzer {
for (final List<String> paths in pathsToSize.keys) { for (final List<String> paths in pathsToSize.keys) {
for (final String path in paths) { for (final String path in paths) {
_SymbolNode childWithPathAsName = currentNode.childByName(path); _SymbolNode? childWithPathAsName = currentNode.childByName(path);
if (childWithPathAsName == null) { if (childWithPathAsName == null) {
childWithPathAsName = _SymbolNode(path); childWithPathAsName = _SymbolNode(path);
...@@ -193,7 +200,7 @@ class SizeAnalyzer { ...@@ -193,7 +200,7 @@ class SizeAnalyzer {
} }
currentNode.addChild(childWithPathAsName); currentNode.addChild(childWithPathAsName);
} }
childWithPathAsName.addSize(pathsToSize[paths]); childWithPathAsName.addSize(pathsToSize[paths] ?? 0);
currentNode = childWithPathAsName; currentNode = childWithPathAsName;
} }
currentNode = rootNode; currentNode = rootNode;
...@@ -240,7 +247,7 @@ class SizeAnalyzer { ...@@ -240,7 +247,7 @@ class SizeAnalyzer {
/// Go through the AOT gen snapshot size JSON and print out a collapsed summary /// Go through the AOT gen snapshot size JSON and print out a collapsed summary
/// for the first package level. /// for the first package level.
void _printAotSnapshotSummary(_SymbolNode aotSnapshotRoot, {int maxDirectoriesShown = 20, @required int level}) { void _printAotSnapshotSummary(_SymbolNode aotSnapshotRoot, {int maxDirectoriesShown = 20, required int level}) {
_printEntitySize( _printEntitySize(
'Dart AOT symbols accounted decompressed size', 'Dart AOT symbols accounted decompressed size',
byteSize: aotSnapshotRoot.byteSize, byteSize: aotSnapshotRoot.byteSize,
...@@ -273,19 +280,19 @@ class SizeAnalyzer { ...@@ -273,19 +280,19 @@ class SizeAnalyzer {
} }
/// Adds breakdown of aot snapshot data as the children of the node at the given path. /// Adds breakdown of aot snapshot data as the children of the node at the given path.
Map<String, dynamic> _addAotSnapshotDataToAnalysis({ Map<String, Object?> _addAotSnapshotDataToAnalysis({
@required Map<String, dynamic> apkAnalysisJson, required Map<String, Object?> apkAnalysisJson,
@required List<String> path, required List<String> path,
@required Map<String, dynamic> aotSnapshotJson, required Map<String, Object?> aotSnapshotJson,
@required Map<String, dynamic> precompilerTrace, required Map<String, Object?> precompilerTrace,
}) { }) {
Map<String, dynamic> currentLevel = apkAnalysisJson; Map<String, Object?> currentLevel = apkAnalysisJson;
currentLevel['precompiler-trace'] = precompilerTrace; currentLevel['precompiler-trace'] = precompilerTrace;
while (path.isNotEmpty) { while (path.isNotEmpty) {
final List<Map<String, dynamic>> children = currentLevel['children'] as List<Map<String, dynamic>>; final List<Map<String, Object?>>? children = currentLevel['children'] as List<Map<String, Object?>>?;
final Map<String, dynamic> childWithPathAsName = children.firstWhere( final Map<String, Object?> childWithPathAsName = children?.firstWhere(
(Map<String, dynamic> child) => child['n'] as String == path.first, (Map<String, Object?> child) => (child['n'] as String?) == path.first,
); ) ?? <String, Object?>{};
path.removeAt(0); path.removeAt(0);
currentLevel = childWithPathAsName; currentLevel = childWithPathAsName;
} }
...@@ -298,8 +305,8 @@ class SizeAnalyzer { ...@@ -298,8 +305,8 @@ class SizeAnalyzer {
/// Print an entity's name with its size on the same line. /// Print an entity's name with its size on the same line.
void _printEntitySize( void _printEntitySize(
String entityName, { String entityName, {
@required int byteSize, required int byteSize,
@required int level, required int level,
bool showColor = true, bool showColor = true,
bool emphasis = false, bool emphasis = false,
}) { }) {
...@@ -354,13 +361,13 @@ class SizeAnalyzer { ...@@ -354,13 +361,13 @@ class SizeAnalyzer {
} }
} }
_SymbolNode _parseAotSnapshot(Map<String, dynamic> aotSnapshotJson) { _SymbolNode? _parseAotSnapshot(Map<String, Object?> aotSnapshotJson) {
final bool isLeafNode = aotSnapshotJson['children'] == null; final bool isLeafNode = aotSnapshotJson['children'] == null;
if (!isLeafNode) { if (!isLeafNode) {
return _buildNodeWithChildren(aotSnapshotJson); return _buildNodeWithChildren(aotSnapshotJson);
} else { } else {
// TODO(peterdjlee): Investigate why there are leaf nodes with size of null. // TODO(peterdjlee): Investigate why there are leaf nodes with size of null.
final int byteSize = aotSnapshotJson['value'] as int; final int? byteSize = aotSnapshotJson['value'] as int?;
if (byteSize == null) { if (byteSize == null) {
return null; return null;
} }
...@@ -369,11 +376,11 @@ class SizeAnalyzer { ...@@ -369,11 +376,11 @@ class SizeAnalyzer {
} }
_SymbolNode _buildNode( _SymbolNode _buildNode(
Map<String, dynamic> aotSnapshotJson, Map<String, Object?> aotSnapshotJson,
int byteSize, { int byteSize, {
List<_SymbolNode> children = const <_SymbolNode>[], List<_SymbolNode> children = const <_SymbolNode>[],
}) { }) {
final String name = aotSnapshotJson['n'] as String; final String name = aotSnapshotJson['n']! as String;
final Map<String, _SymbolNode> childrenMap = <String, _SymbolNode>{}; final Map<String, _SymbolNode> childrenMap = <String, _SymbolNode>{};
for (final _SymbolNode child in children) { for (final _SymbolNode child in children) {
...@@ -388,17 +395,22 @@ class SizeAnalyzer { ...@@ -388,17 +395,22 @@ class SizeAnalyzer {
/// Builds a node by recursively building all of its children first /// Builds a node by recursively building all of its children first
/// in order to calculate the sum of its children's sizes. /// in order to calculate the sum of its children's sizes.
_SymbolNode _buildNodeWithChildren(Map<String, dynamic> aotSnapshotJson) { _SymbolNode? _buildNodeWithChildren(Map<String, Object?> aotSnapshotJson) {
final List<dynamic> rawChildren = aotSnapshotJson['children'] as List<dynamic>; final List<Object?> rawChildren = aotSnapshotJson['children'] as List<Object?>? ?? <Object?>[];
final List<_SymbolNode> symbolNodeChildren = <_SymbolNode>[]; final List<_SymbolNode> symbolNodeChildren = <_SymbolNode>[];
int totalByteSize = 0; int totalByteSize = 0;
// Given a child, build its subtree. // Given a child, build its subtree.
for (final dynamic child in rawChildren) { for (final Object? child in rawChildren) {
final _SymbolNode childTreemapNode = _parseAotSnapshot(child as Map<String, dynamic>); if (child == null) {
continue;
}
final _SymbolNode? childTreemapNode = _parseAotSnapshot(child as Map<String, Object?>);
if (childTreemapNode != null) {
symbolNodeChildren.add(childTreemapNode); symbolNodeChildren.add(childTreemapNode);
totalByteSize += childTreemapNode.byteSize; totalByteSize += childTreemapNode.byteSize;
} }
}
// If none of the children matched the diff tree type // If none of the children matched the diff tree type
if (totalByteSize == 0) { if (totalByteSize == 0) {
...@@ -430,13 +442,13 @@ class _SymbolNode { ...@@ -430,13 +442,13 @@ class _SymbolNode {
byteSize += sizeToBeAdded; byteSize += sizeToBeAdded;
} }
_SymbolNode get parent => _parent; _SymbolNode? get parent => _parent;
_SymbolNode _parent; _SymbolNode? _parent;
Iterable<_SymbolNode> get children => _children.values; Iterable<_SymbolNode> get children => _children.values;
final Map<String, _SymbolNode> _children; final Map<String, _SymbolNode> _children;
_SymbolNode childByName(String name) => _children[name]; _SymbolNode? childByName(String name) => _children[name];
_SymbolNode addChild(_SymbolNode child) { _SymbolNode addChild(_SymbolNode child) {
assert(child.parent == null); assert(child.parent == null);
...@@ -452,12 +464,12 @@ class _SymbolNode { ...@@ -452,12 +464,12 @@ class _SymbolNode {
children.forEach(addChild); children.forEach(addChild);
} }
Map<String, dynamic> toJson() { Map<String, Object?> toJson() {
final Map<String, dynamic> json = <String, dynamic>{ final Map<String, Object?> json = <String, Object?>{
'n': name, 'n': name,
'value': byteSize 'value': byteSize
}; };
final List<Map<String, dynamic>> childrenAsJson = <Map<String, dynamic>>[]; final List<Map<String, Object?>> childrenAsJson = <Map<String, Object?>>[];
for (final _SymbolNode child in children) { for (final _SymbolNode child in children) {
childrenAsJson.add(child.toJson()); childrenAsJson.add(child.toJson());
} }
...@@ -470,7 +482,7 @@ class _SymbolNode { ...@@ -470,7 +482,7 @@ class _SymbolNode {
/// Matches `pattern` against the entirety of `string`. /// Matches `pattern` against the entirety of `string`.
@visibleForTesting @visibleForTesting
Match matchesPattern(String string, {@required Pattern pattern}) { Match? matchesPattern(String string, {required Pattern pattern}) {
final Match match = pattern.matchAsPrefix(string); final Match? match = pattern.matchAsPrefix(string);
return (match != null && match.end == string.length) ? match : null; return (match != null && match.end == string.length) ? match : null;
} }
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:archive/archive.dart'; import 'package:archive/archive.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/analyze_size.dart'; import 'package:flutter_tools/src/base/analyze_size.dart';
...@@ -43,15 +41,15 @@ const String aotSizeOutput = ''' ...@@ -43,15 +41,15 @@ const String aotSizeOutput = '''
'''; ''';
void main() { void main() {
MemoryFileSystem fileSystem; late MemoryFileSystem fileSystem;
BufferLogger logger; late BufferLogger logger;
setUp(() { setUp(() {
fileSystem = MemoryFileSystem.test(); fileSystem = MemoryFileSystem.test();
logger = BufferLogger.test(); logger = BufferLogger.test();
}); });
test('matchesPattern matches only entire strings', () { testWithoutContext('matchesPattern matches only entire strings', () {
expect(matchesPattern('', pattern: ''), isNotNull); expect(matchesPattern('', pattern: ''), isNotNull);
expect(matchesPattern('', pattern: 'foo'), null); expect(matchesPattern('', pattern: 'foo'), null);
expect(matchesPattern('foo', pattern: ''), null); expect(matchesPattern('foo', pattern: ''), null);
...@@ -62,7 +60,7 @@ void main() { ...@@ -62,7 +60,7 @@ void main() {
expect(matchesPattern('foobar', pattern: RegExp(r'.*b')), null); expect(matchesPattern('foobar', pattern: RegExp(r'.*b')), null);
}); });
test('builds APK analysis correctly', () async { testWithoutContext('builds APK analysis correctly', () async {
final SizeAnalyzer sizeAnalyzer = SizeAnalyzer( final SizeAnalyzer sizeAnalyzer = SizeAnalyzer(
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
...@@ -78,7 +76,7 @@ void main() { ...@@ -78,7 +76,7 @@ void main() {
..addFile(ArchiveFile('lib/arm64-v8a/libflutter.so', 50, List<int>.filled(50, 0))); ..addFile(ArchiveFile('lib/arm64-v8a/libflutter.so', 50, List<int>.filled(50, 0)));
final File apk = fileSystem.file('test.apk') final File apk = fileSystem.file('test.apk')
..writeAsBytesSync(ZipEncoder().encode(archive)); ..writeAsBytesSync(ZipEncoder().encode(archive)!);
final File aotSizeJson = fileSystem.file('test.json') final File aotSizeJson = fileSystem.file('test.json')
..createSync() ..createSync()
..writeAsStringSync(aotSizeOutput); ..writeAsStringSync(aotSizeOutput);
...@@ -139,7 +137,7 @@ void main() { ...@@ -139,7 +137,7 @@ void main() {
expect(result['precompiler-trace'], <String, Object>{}); expect(result['precompiler-trace'], <String, Object>{});
}); });
test('outputs summary to command line correctly', () async { testWithoutContext('outputs summary to command line correctly', () async {
final SizeAnalyzer sizeAnalyzer = SizeAnalyzer( final SizeAnalyzer sizeAnalyzer = SizeAnalyzer(
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
...@@ -155,7 +153,7 @@ void main() { ...@@ -155,7 +153,7 @@ void main() {
..addFile(ArchiveFile('lib/arm64-v8a/libflutter.so', 50, List<int>.filled(50, 0))); ..addFile(ArchiveFile('lib/arm64-v8a/libflutter.so', 50, List<int>.filled(50, 0)));
final File apk = fileSystem.file('test.apk') final File apk = fileSystem.file('test.apk')
..writeAsBytesSync(ZipEncoder().encode(archive)); ..writeAsBytesSync(ZipEncoder().encode(archive)!);
final File aotSizeJson = fileSystem.file('test.json') final File aotSizeJson = fileSystem.file('test.json')
..writeAsStringSync(aotSizeOutput); ..writeAsStringSync(aotSizeOutput);
final File precompilerTrace = fileSystem.file('trace.json') final File precompilerTrace = fileSystem.file('trace.json')
...@@ -181,7 +179,7 @@ void main() { ...@@ -181,7 +179,7 @@ void main() {
); );
}); });
test('can analyze contents of output directory', () async { testWithoutContext('can analyze contents of output directory', () async {
final SizeAnalyzer sizeAnalyzer = SizeAnalyzer( final SizeAnalyzer sizeAnalyzer = SizeAnalyzer(
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
...@@ -202,7 +200,7 @@ void main() { ...@@ -202,7 +200,7 @@ void main() {
final File precompilerTrace = fileSystem.file('trace.json') final File precompilerTrace = fileSystem.file('trace.json')
..writeAsStringSync('{}'); ..writeAsStringSync('{}');
final Map<String, Object> result = await sizeAnalyzer.analyzeAotSnapshot( final Map<String, Object?> result = await sizeAnalyzer.analyzeAotSnapshot(
outputDirectory: outputDirectory, outputDirectory: outputDirectory,
aotSnapshot: aotSizeJson, aotSnapshot: aotSizeJson,
precompilerTrace: precompilerTrace, precompilerTrace: precompilerTrace,
...@@ -223,4 +221,36 @@ void main() { ...@@ -223,4 +221,36 @@ void main() {
expect(result['type'], 'linux'); expect(result['type'], 'linux');
expect(result['precompiler-trace'], <String, Object>{}); expect(result['precompiler-trace'], <String, Object>{});
}); });
testWithoutContext('handles null AOT snapshot json', () async {
final SizeAnalyzer sizeAnalyzer = SizeAnalyzer(
fileSystem: fileSystem,
logger: logger,
appFilenamePattern: RegExp(r'lib.*app\.so'),
flutterUsage: TestUsage(),
);
final Directory outputDirectory = fileSystem.directory('example/out/foo.app')..createSync(recursive: true);
final File invalidAotSizeJson = fileSystem.file('test.json')..writeAsStringSync('null');
final File precompilerTrace = fileSystem.file('trace.json');
await expectLater(
() => sizeAnalyzer.analyzeAotSnapshot(
outputDirectory: outputDirectory,
aotSnapshot: invalidAotSizeJson,
precompilerTrace: precompilerTrace,
type: 'linux',
),
throwsToolExit());
final File apk = fileSystem.file('test.apk')..writeAsBytesSync(ZipEncoder().encode(Archive())!);
await expectLater(
() => sizeAnalyzer.analyzeZipSizeAndAotSnapshot(
zipFile: apk,
aotSnapshot: invalidAotSizeJson,
precompilerTrace: precompilerTrace,
kind: 'apk',
),
throwsToolExit());
});
} }
...@@ -9,6 +9,7 @@ import 'package:file_testing/file_testing.dart'; ...@@ -9,6 +9,7 @@ import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart';
import 'test_utils.dart'; import 'test_utils.dart';
const String apkDebugMessage = 'A summary of your APK analysis can be found at: '; const String apkDebugMessage = 'A summary of your APK analysis can be found at: ';
...@@ -16,7 +17,7 @@ const String iosDebugMessage = 'A summary of your iOS bundle analysis can be fou ...@@ -16,7 +17,7 @@ const String iosDebugMessage = 'A summary of your iOS bundle analysis can be fou
const String runDevToolsMessage = 'flutter pub global activate devtools; flutter pub global run devtools '; const String runDevToolsMessage = 'flutter pub global activate devtools; flutter pub global run devtools ';
void main() { void main() {
testWithoutContext('--analyze-size flag produces expected output on hello_world for Android', () async { testUsingContext('--analyze-size flag produces expected output on hello_world for Android', () async {
final String workingDirectory = fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world'); final String workingDirectory = fileSystem.path.join(getFlutterRoot(), 'examples', 'hello_world');
final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter'); final String flutterBin = fileSystem.path.join(getFlutterRoot(), 'bin', 'flutter');
final ProcessResult result = await processManager.run(<String>[ final ProcessResult result = await processManager.run(<String>[
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment