433 lines
13 KiB
Dart
433 lines
13 KiB
Dart
/**
|
||
* 大电路性能压测基准
|
||
*
|
||
* 测试场景:1000/5000/10000 元件设计
|
||
* 测试指标:启动时间、帧率、内存占用、操作延迟
|
||
* 瓶颈分析:渲染/数据/手势识别
|
||
*
|
||
* @version 1.0.0
|
||
* @date 2026-03-07
|
||
* @author 性能优化专家
|
||
*/
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'dart:math' as math;
|
||
import '../../lib/domain/models/core_models.dart';
|
||
import '../../lib/presentation/components/editable_canvas.dart';
|
||
import '../../lib/domain/managers/selection_manager.dart';
|
||
|
||
// ============================================================================
|
||
// 性能指标模型
|
||
// ============================================================================
|
||
|
||
/// 性能测试结果
|
||
class PerformanceMetrics {
|
||
final int componentCount;
|
||
final Duration startupTime;
|
||
final double averageFPS;
|
||
final int minFPS;
|
||
final int maxFPS;
|
||
final int memoryUsageMB;
|
||
final Duration panLatency;
|
||
final Duration zoomLatency;
|
||
final Duration tapLatency;
|
||
final Duration dragLatency;
|
||
final String bottleneck;
|
||
final Map<String, dynamic> details;
|
||
|
||
PerformanceMetrics({
|
||
required this.componentCount,
|
||
required this.startupTime,
|
||
required this.averageFPS,
|
||
required this.minFPS,
|
||
required this.maxFPS,
|
||
required this.memoryUsageMB,
|
||
required this.panLatency,
|
||
required this.zoomLatency,
|
||
required this.tapLatency,
|
||
required this.dragLatency,
|
||
required this.bottleneck,
|
||
required this.details,
|
||
});
|
||
|
||
@override
|
||
String toString() {
|
||
return '''
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
性能报告 - ${componentCount} 元件
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
启动时间:${startupTime.inMilliseconds}ms
|
||
帧率:${averageFPS.toStringAsFixed(1)} FPS (min: $minFPS, max: $maxFPS)
|
||
内存占用:${memoryUsageMB}MB
|
||
|
||
操作延迟:
|
||
- 平移:${panLatency.inMilliseconds}ms
|
||
- 缩放:${zoomLatency.inMilliseconds}ms
|
||
- 点击:${tapLatency.inMilliseconds}ms
|
||
- 拖拽:${dragLatency.inMilliseconds}ms
|
||
|
||
瓶颈分析:$bottleneck
|
||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||
''';
|
||
}
|
||
|
||
Map<String, dynamic> toJson() {
|
||
return {
|
||
'componentCount': componentCount,
|
||
'startupTime': startupTime.inMilliseconds,
|
||
'averageFPS': averageFPS,
|
||
'minFPS': minFPS,
|
||
'maxFPS': maxFPS,
|
||
'memoryUsageMB': memoryUsageMB,
|
||
'panLatency': panLatency.inMilliseconds,
|
||
'zoomLatency': zoomLatency.inMilliseconds,
|
||
'tapLatency': tapLatency.inMilliseconds,
|
||
'dragLatency': dragLatency.inMilliseconds,
|
||
'bottleneck': bottleneck,
|
||
'details': details,
|
||
};
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 测试数据生成器
|
||
// ============================================================================
|
||
|
||
/// 测试电路生成器
|
||
class TestCircuitGenerator {
|
||
static Design generateDesign({
|
||
required int componentCount,
|
||
String name = 'Performance Test',
|
||
}) {
|
||
final timestamp = DateTime.now().millisecondsSinceEpoch;
|
||
final random = math.Random(42); // 固定种子保证可重复性
|
||
|
||
final components = <ID, Component>{};
|
||
final nets = <ID, Net>{};
|
||
|
||
// 生成元件
|
||
for (int i = 0; i < componentCount; i++) {
|
||
final id = 'comp_$i';
|
||
final x = random.nextInt(10000) * 1000; // 0-10000mm
|
||
final y = random.nextInt(10000) * 1000;
|
||
|
||
final pinCount = 4 + random.nextInt(12); // 4-16 pins
|
||
final pins = <PinReference>[];
|
||
for (int p = 0; p < pinCount; p++) {
|
||
pins.add(PinReference(
|
||
pinId: 'pin_$p',
|
||
name: 'P$p',
|
||
x: (p % 4) * 100000 - 150000,
|
||
y: (p ~/ 4) * 100000 - 150000,
|
||
rotation: 0,
|
||
electricalType: PinElectricalType.passive,
|
||
));
|
||
}
|
||
|
||
components[id] = Component(
|
||
id: id,
|
||
name: 'U$i',
|
||
type: ComponentType.ic,
|
||
value: 'TEST_${i % 100}',
|
||
footprint: Footprint(
|
||
id: 'fp_$i',
|
||
name: 'TEST_FP',
|
||
pads: [],
|
||
metadata: Metadata(
|
||
createdAt: timestamp,
|
||
updatedAt: timestamp,
|
||
),
|
||
),
|
||
position: Position2D(x: x, y: y),
|
||
layerId: 'layer_signal_1',
|
||
rotation: 0,
|
||
mirror: MirrorState.none,
|
||
pins: pins,
|
||
metadata: Metadata(
|
||
createdAt: timestamp,
|
||
updatedAt: timestamp,
|
||
),
|
||
);
|
||
}
|
||
|
||
// 生成网络连接(每个元件随机连接 1-3 个其他元件)
|
||
int netIndex = 0;
|
||
components.forEach((id, component) {
|
||
final connectionCount = 1 + random.nextInt(3);
|
||
for (int c = 0; c < connectionCount; c++) {
|
||
final targetId = components.keys.elementAt(
|
||
random.nextInt(components.length),
|
||
);
|
||
if (targetId == id) continue;
|
||
|
||
final targetComponent = components[targetId]!;
|
||
|
||
final netId = 'net_${netIndex++}';
|
||
nets[netId] = Net(
|
||
id: netId,
|
||
name: 'N$netIndex',
|
||
type: NetType.signal,
|
||
connections: [
|
||
ConnectionPoint(
|
||
id: '$id:pin_0',
|
||
type: ConnectionType.pin,
|
||
componentId: id,
|
||
pinId: 'pin_0',
|
||
position: Position2D(
|
||
x: component.position.x + component.pins[0].x,
|
||
y: component.position.y + component.pins[0].y,
|
||
),
|
||
layerId: component.layerId,
|
||
),
|
||
ConnectionPoint(
|
||
id: '$targetId:pin_0',
|
||
type: ConnectionType.pin,
|
||
componentId: targetId,
|
||
pinId: 'pin_0',
|
||
position: Position2D(
|
||
x: targetComponent.position.x + targetComponent.pins[0].x,
|
||
y: targetComponent.position.y + targetComponent.pins[0].y,
|
||
),
|
||
layerId: targetComponent.layerId,
|
||
),
|
||
],
|
||
metadata: Metadata(
|
||
createdAt: timestamp,
|
||
updatedAt: timestamp,
|
||
),
|
||
);
|
||
}
|
||
});
|
||
|
||
return Design(
|
||
id: 'perf_test_design',
|
||
name: name,
|
||
version: '1.0.0',
|
||
components: components,
|
||
nets: nets,
|
||
layers: {
|
||
'layer_signal_1': Layer(
|
||
id: 'layer_signal_1',
|
||
name: 'Signal Layer 1',
|
||
type: LayerType.signal,
|
||
stackupOrder: 1,
|
||
metadata: Metadata(
|
||
createdAt: timestamp,
|
||
updatedAt: timestamp,
|
||
),
|
||
),
|
||
},
|
||
designRules: DesignRules(),
|
||
createdAt: timestamp,
|
||
updatedAt: timestamp,
|
||
);
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 性能测试工具
|
||
// ============================================================================
|
||
|
||
/// FPS 计数器
|
||
class FPSCounter {
|
||
final List<int> _frameTimes = [];
|
||
DateTime? _lastFrameTime;
|
||
int _frameCount = 0;
|
||
|
||
void markFrame() {
|
||
final now = DateTime.now();
|
||
if (_lastFrameTime != null) {
|
||
_frameTimes.add(now.difference(_lastFrameTime!).inMilliseconds);
|
||
if (_frameTimes.length > 300) {
|
||
_frameTimes.removeAt(0);
|
||
}
|
||
}
|
||
_lastFrameTime = now;
|
||
_frameCount++;
|
||
}
|
||
|
||
double get averageFPS {
|
||
if (_frameTimes.isEmpty) return 0.0;
|
||
final avgFrameTime = _frameTimes.reduce((a, b) => a + b) / _frameTimes.length;
|
||
return avgFrameTime > 0 ? 1000.0 / avgFrameTime : 0.0;
|
||
}
|
||
|
||
int get minFPS {
|
||
if (_frameTimes.isEmpty) return 0;
|
||
final maxFrameTime = _frameTimes.reduce(math.max);
|
||
return maxFrameTime > 0 ? (1000.0 / maxFrameTime).floor() : 0;
|
||
}
|
||
|
||
int get maxFPS {
|
||
if (_frameTimes.isEmpty) return 0;
|
||
final minFrameTime = _frameTimes.reduce(math.min);
|
||
return minFrameTime > 0 ? (1000.0 / minFrameTime).floor() : 0;
|
||
}
|
||
|
||
void reset() {
|
||
_frameTimes.clear();
|
||
_lastFrameTime = null;
|
||
_frameCount = 0;
|
||
}
|
||
}
|
||
|
||
// ============================================================================
|
||
// 基准测试
|
||
// ============================================================================
|
||
|
||
void main() {
|
||
group('大电路性能基准测试', () {
|
||
testWidgets('1000 元件 - 启动性能', (WidgetTester tester) async {
|
||
final design = TestCircuitGenerator.generateDesign(componentCount: 1000);
|
||
final selectionManager = SelectionManager();
|
||
|
||
final stopwatch = Stopwatch()..start();
|
||
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
home: Scaffold(
|
||
body: EditableCanvas(
|
||
design: design,
|
||
onDesignChanged: (newDesign) {},
|
||
selectionManager: selectionManager,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
await tester.pumpAndSettle();
|
||
stopwatch.stop();
|
||
|
||
final startupTime = stopwatch.elapsed;
|
||
print('✓ 1000 元件启动时间:${startupTime.inMilliseconds}ms');
|
||
|
||
expect(startupTime.inMilliseconds, lessThan(3000),
|
||
reason: '1000 元件启动时间应小于 3 秒');
|
||
});
|
||
|
||
testWidgets('1000 元件 - 平移性能', (WidgetTester tester) async {
|
||
final design = TestCircuitGenerator.generateDesign(componentCount: 1000);
|
||
final selectionManager = SelectionManager();
|
||
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
home: Scaffold(
|
||
body: EditableCanvas(
|
||
design: design,
|
||
onDesignChanged: (newDesign) {},
|
||
selectionManager: selectionManager,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
await tester.pumpAndSettle();
|
||
|
||
final fpsCounter = FPSCounter();
|
||
final stopwatch = Stopwatch();
|
||
|
||
// 执行平移操作
|
||
stopwatch.start();
|
||
final gesture = await tester.startGesture(const Offset(200, 200));
|
||
for (int i = 0; i < 10; i++) {
|
||
await gesture.moveBy(const Offset(50, 0));
|
||
await tester.pump();
|
||
fpsCounter.markFrame();
|
||
}
|
||
await gesture.up();
|
||
stopwatch.stop();
|
||
|
||
final avgLatency = stopwatch.elapsedMilliseconds ~/ 10;
|
||
print('✓ 1000 元件平移延迟:${avgLatency}ms, FPS: ${fpsCounter.averageFPS.toStringAsFixed(1)}');
|
||
|
||
expect(avgLatency, lessThan(50),
|
||
reason: '平移延迟应小于 50ms');
|
||
expect(fpsCounter.averageFPS, greaterThan(30),
|
||
reason: '帧率应大于 30 FPS');
|
||
});
|
||
|
||
testWidgets('5000 元件 - 启动性能', (WidgetTester tester) async {
|
||
final design = TestCircuitGenerator.generateDesign(componentCount: 5000);
|
||
final selectionManager = SelectionManager();
|
||
|
||
final stopwatch = Stopwatch()..start();
|
||
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
home: Scaffold(
|
||
body: EditableCanvas(
|
||
design: design,
|
||
onDesignChanged: (newDesign) {},
|
||
selectionManager: selectionManager,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
await tester.pumpAndSettle();
|
||
stopwatch.stop();
|
||
|
||
final startupTime = stopwatch.elapsed;
|
||
print('✓ 5000 元件启动时间:${startupTime.inMilliseconds}ms');
|
||
|
||
// 5000 元件允许更长的启动时间
|
||
expect(startupTime.inMilliseconds, lessThan(10000),
|
||
reason: '5000 元件启动时间应小于 10 秒');
|
||
});
|
||
|
||
testWidgets('10000 元件 - 启动性能', (WidgetTester tester) async {
|
||
final design = TestCircuitGenerator.generateDesign(componentCount: 10000);
|
||
final selectionManager = SelectionManager();
|
||
|
||
final stopwatch = Stopwatch()..start();
|
||
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
home: Scaffold(
|
||
body: EditableCanvas(
|
||
design: design,
|
||
onDesignChanged: (newDesign) {},
|
||
selectionManager: selectionManager,
|
||
),
|
||
),
|
||
),
|
||
);
|
||
|
||
await tester.pumpAndSettle();
|
||
stopwatch.stop();
|
||
|
||
final startupTime = stopwatch.elapsed;
|
||
print('✓ 10000 元件启动时间:${startupTime.inMilliseconds}ms');
|
||
|
||
// 10000 元件需要优化,这里先记录基线
|
||
print('⚠ 10000 元件启动时间基线:${startupTime.inMilliseconds}ms (待优化)');
|
||
});
|
||
|
||
test('生成性能基准报告', () {
|
||
// 这是一个示例报告生成测试
|
||
final metrics = PerformanceMetrics(
|
||
componentCount: 1000,
|
||
startupTime: const Duration(milliseconds: 1500),
|
||
averageFPS: 55.5,
|
||
minFPS: 45,
|
||
maxFPS: 60,
|
||
memoryUsageMB: 128,
|
||
panLatency: const Duration(milliseconds: 16),
|
||
zoomLatency: const Duration(milliseconds: 20),
|
||
tapLatency: const Duration(milliseconds: 8),
|
||
dragLatency: const Duration(milliseconds: 24),
|
||
bottleneck: '渲染性能',
|
||
details: {
|
||
'renderTime': 12,
|
||
'layoutTime': 4,
|
||
'gestureTime': 2,
|
||
},
|
||
);
|
||
|
||
print(metrics);
|
||
print('JSON: ${metrics.toJson()}');
|
||
});
|
||
});
|
||
}
|