blob: e1840c7dafb23b66ca88ff122ddce5e5ea34363f [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. 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:analyzer/src/manifest/manifest_validator.dart';
import 'package:analyzer/src/utilities/extensions/object.dart';
import 'package:collection/collection.dart';
import '../../../test/util/tree_string_sink.dart';
String formatSizeInBytes(int value) {
var buffer = StringBuffer();
buffer.write('$value');
var kb = value ~/ 1024;
if (kb.abs() > 0) {
buffer.write(' = $kb KB');
}
var mb = kb / 1024;
if (mb.abs() >= 1.0) {
buffer.write(' = ${mb.toStringAsFixed(1)} MB');
}
var gb = mb / 1024;
if (gb.abs() >= 1.0) {
buffer.write(' = ${gb.toStringAsFixed(2)} GB');
}
return buffer.toString();
}
void _writeDisplayText(
TreeStringSink sink,
BenchmarkResult result,
BenchmarkResult? base,
) {
switch (result) {
case BenchmarkResultBytes():
var sizeStr = formatSizeInBytes(result.value);
sink.writelnWithIndent('${result.name}: $sizeStr');
if (base is BenchmarkResultBytes) {
var diff = result.value - base.value;
if (diff != 0) {
sink.withIndent(() {
var diffStr = formatSizeInBytes(diff);
var diffPercent = 100 * diff / base.value;
var diffPercentStr = diffPercent.toStringAsFixed(2);
sink.writelnWithIndent('change: $diffPercentStr% $diffStr');
});
}
}
case BenchmarkResultCompound():
sink.writelnWithIndent(result.name);
sink.withIndent(() {
for (var child in result.children) {
var childBase = base
.ifTypeOrNull<BenchmarkResultCompound>()
?.children
.firstWhereOrNull((e) => e.name == child.name);
_writeDisplayText(sink, child, childBase);
}
});
case BenchmarkResultCount():
sink.writelnWithIndent('${result.name}: ${result.value}');
if (base is BenchmarkResultCount) {
var diff = result.value - base.value;
if (diff != 0) {
sink.withIndent(() {
var diffPercent = 100 * diff / base.value;
var diffPercentStr = diffPercent.toStringAsFixed(2);
sink.writelnWithIndent('change: $diffPercentStr% $diff');
});
}
}
}
}
void _writeXmlText(TreeStringSink sink, BenchmarkResult result) {
switch (result) {
case BenchmarkResultBytes():
sink.writelnWithIndent(
"<bytes name='${result.name}' value='${result.value}'/>",
);
case BenchmarkResultCompound():
sink.writelnWithIndent("<compound name='${result.name}'>");
sink.withIndent(() {
for (var child in result.children) {
_writeXmlText(sink, child);
}
});
sink.writelnWithIndent('</compound>');
case BenchmarkResultCount():
sink.writelnWithIndent(
"<count name='${result.name}' value='${result.value}'/>",
);
}
}
sealed class BenchmarkResult {
final String name;
BenchmarkResult({
required this.name,
});
String get asXmlText {
var buffer = StringBuffer();
var sink = TreeStringSink(sink: buffer, indent: '');
_writeXmlText(sink, this);
return buffer.toString();
}
String asDisplayText(BenchmarkResult? base) {
var buffer = StringBuffer();
var sink = TreeStringSink(sink: buffer, indent: '');
_writeDisplayText(sink, this, base);
return buffer.toString();
}
static BenchmarkResult fromXmlText(String text) {
var parser = ManifestParser.general(text, uri: Uri.parse(''));
var tagResult = parser.parseXmlTag();
return _fromXml(tagResult.element!);
}
static BenchmarkResult _fromXml(XmlElement element) {
var name = element.attributes['name']!.value;
switch (element.name) {
case 'compound':
return BenchmarkResultCompound(
name: name,
children: element.children.map(_fromXml).toList(),
);
case 'count':
return BenchmarkResultCount(
name: name,
value: int.parse(element.attributes['value']!.value),
);
case 'bytes':
return BenchmarkResultBytes(
name: name,
value: int.parse(element.attributes['value']!.value),
);
default:
throw UnimplementedError(element.name);
}
}
}
final class BenchmarkResultBytes extends BenchmarkResult {
final int value;
BenchmarkResultBytes({
required super.name,
required this.value,
});
}
final class BenchmarkResultCompound extends BenchmarkResult {
final List<BenchmarkResult> children = [];
BenchmarkResultCompound({
required super.name,
List<BenchmarkResult>? children,
}) {
if (children != null) {
this.children.addAll(children);
}
}
void add(BenchmarkResult child) {
children.add(child);
}
}
final class BenchmarkResultCount extends BenchmarkResult {
final int value;
BenchmarkResultCount({
required super.name,
required this.value,
});
}