/// 数据格式模块测试 /// /// 测试 Tile 序列化、KiCad 导入、增量保存功能 /// /// @date 2026-03-07 import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:mobile_eda/data/data_format.dart'; void main() { // ============================================================================ // Tile 格式测试 // ============================================================================ group('TileSerializer', () { test('should serialize simple design', () { final design = { 'id': 'design-001', 'name': 'Test Circuit', 'version': '1.0.0', 'components': [ { 'id': 'comp-001', 'name': 'R1', 'type': 'resistor', 'value': '10k', 'position': {'x': 1000000, 'y': 2000000}, }, { 'id': 'comp-002', 'name': 'C1', 'type': 'capacitor', 'value': '100nF', 'position': {'x': 3000000, 'y': 4000000}, }, ], 'metadata': { 'createdAt': 1234567890, 'updatedAt': 1234567890, }, }; final bytes = designToTile(design); expect(bytes, isNotNull); expect(bytes.length, greaterThan(0)); expect(bytes.length, lessThan(1000)); // 压缩后应该很小 }); test('should deserialize to original design', () { final originalDesign = { 'id': 'design-002', 'name': 'Round Trip Test', 'components': [ {'id': 'c1', 'name': 'R1', 'type': 'resistor', 'value': '1k'}, {'id': 'c2', 'name': 'R2', 'type': 'resistor', 'value': '2k'}, ], }; final bytes = designToTile(originalDesign); final restoredDesign = tileToDesign(bytes); expect(restoredDesign['id'], equals(originalDesign['id'])); expect(restoredDesign['name'], equals(originalDesign['name'])); expect(restoredDesign['components'], isA()); expect(restoredDesign['components'].length, equals(2)); }); test('should handle empty design', () { final emptyDesign = { 'id': 'empty', 'name': 'Empty', 'components': [], 'nets': [], }; final bytes = designToTile(emptyDesign); final restored = tileToDesign(bytes); expect(restored['id'], equals('empty')); expect(restored['components'], equals([])); }); test('should compress repeated strings', () { // 创建包含大量重复字符串的设计 final design = { 'id': 'test', 'components': List.generate(100, (i) => { 'id': 'comp-$i', 'name': 'R$i', 'type': 'resistor', // 重复字符串 'value': '10k', // 重复字符串 }), }; final bytes = designToTile(design); final jsonSize = design.toString().length; final tileSize = bytes.length; // Tile 格式应该显著小于 JSON expect(tileSize, lessThan(jsonSize)); print('Compression: JSON=$jsonSize bytes, Tile=$tileSize bytes, ratio=${tileSize/jsonSize*100}%'); }); test('should handle coordinate delta encoding', () { // 创建坐标连续的设计 final design = { 'id': 'trace-test', 'trace': { 'points': [ {'x': 0, 'y': 0}, {'x': 100, 'y': 100}, {'x': 200, 'y': 150}, {'x': 350, 'y': 200}, {'x': 500, 'y': 300}, ], }, }; final bytes = designToTile(design); final restored = tileToDesign(bytes); expect(restored['trace']['points'], isA()); expect(restored['trace']['points'].length, equals(5)); }); }); // ============================================================================ // KiCad 导入器测试 // ============================================================================ group('KicadImporter', () { test('should parse simple kicad schematic', () { final content = ''' (kicad_sch (version 20211014) (component (reference "R1") (value "10k") (footprint "Resistor_SMD:R_0805") (at 100 200 0) ) (component (reference "C1") (value "100nF") (footprint "Capacitor_SMD:C_0805") (at 300 400 90) ) ) '''; final importer = KicadImporter(); final schematic = importer.import(content); expect(schematic.version, equals('20211014')); expect(schematic.components.length, equals(2)); expect(schematic.components[0].reference, equals('R1')); expect(schematic.components[0].value, equals('10k')); expect(schematic.components[0].x, equals(100)); expect(schematic.components[0].y, equals(200)); }); test('should convert kicad to EDA design', () { final content = ''' (kicad_sch (version 20211014) (component (reference "U1") (value "ATmega328P") (footprint "Package_DIP:DIP-28") ) (net "VCC" (node (pin "U1" "VCC")) ) (net "GND" (node (pin "U1" "GND")) ) ) '''; final design = importKicadSchematic(content); expect(design['id'], isNotNull); expect(design['name'], equals('Imported from KiCad')); expect(design['tables']['components'], isA()); expect(design['tables']['nets'], isA()); }); test('should handle component with pins', () { final content = ''' (kicad_sch (version 20211014) (component (reference "U1") (value "ATmega328P") (pin (at 0 0 0) (property "Name" "VCC") (property "Number" "7")) (pin (at 0 10 0) (property "Name" "GND") (property "Number" "8")) ) ) '''; final importer = KicadImporter(); final schematic = importer.import(content); expect(schematic.components.length, equals(1)); expect(schematic.components[0].pins.length, equals(2)); }); }); // ============================================================================ // 增量保存测试 // ============================================================================ group('IncrementalSave', () { test('should record operations', () { final manager = createIncrementalSaveManager(); manager.recordOperation(AddComponentCommand( component: {'id': 'c1', 'name': 'R1'}, )); expect(manager.history.canUndo, isTrue); expect(manager.history.undoStackSize, equals(1)); }); test('should support undo', () { final manager = createIncrementalSaveManager(); manager.recordOperation(AddComponentCommand( component: {'id': 'c1', 'name': 'R1'}, )); expect(manager.history.canUndo, isTrue); manager.history.undo(); expect(manager.history.canUndo, isFalse); expect(manager.history.canRedo, isTrue); }); test('should support redo', () { final manager = createIncrementalSaveManager(); manager.recordOperation(AddComponentCommand( component: {'id': 'c1', 'name': 'R1'}, )); manager.history.undo(); expect(manager.history.canRedo, isTrue); manager.history.redo(); expect(manager.history.canRedo, isFalse); expect(manager.history.canUndo, isTrue); }); test('should create snapshot', () { final manager = createIncrementalSaveManager(); final design = { 'id': 'test', 'components': [{'id': 'c1', 'name': 'R1'}], }; manager.setCurrentState(design); manager.createSnapshot(design); expect(manager.lastSnapshot, isNotNull); expect(manager.isDirty, isFalse); }); test('should save and restore', () { final manager = createIncrementalSaveManager(); final design = { 'id': 'test', 'components': [{'id': 'c1', 'name': 'R1'}], }; manager.setCurrentState(design); manager.createSnapshot(design); // 添加一些操作 manager.recordOperation(MoveComponentCommand( componentId: 'c1', oldX: 0, oldY: 0, newX: 100, newY: 100, )); // 保存 final saveData = manager.save(); expect(saveData.snapshot, isNotNull); expect(saveData.deltaLog.length, equals(1)); // 恢复 manager.restore(saveData); expect(manager.currentState, isNotNull); }); test('should serialize save data', () { final saveData = IncrementalSaveData( snapshot: {'id': 'test'}, deltaLog: [ {'type': 'componentMove', 'componentId': 'c1'}, ], timestamp: DateTime.now(), ); final bytes = saveData.toBytes(); final restored = IncrementalSaveData.fromBytes(bytes); expect(restored.snapshot, isNotNull); expect(restored.deltaLog.length, equals(1)); expect(restored.timestamp.millisecondsSinceEpoch, equals(saveData.timestamp.millisecondsSinceEpoch)); }); }); // ============================================================================ // 断点恢复测试 // ============================================================================ group('CheckpointManager', () { test('should create checkpoint', () { final manager = createCheckpointManager(); final state = {'id': 'test', 'value': 123}; manager.createCheckpoint('test_checkpoint', state, 'Test reason'); expect(manager.checkpoints.length, equals(1)); }); test('should restore from checkpoint', () { final manager = createCheckpointManager(); final state = {'id': 'test', 'value': 123}; manager.createCheckpoint('before_change', state, 'Before modification'); final restored = manager.restoreFromCheckpoint('before_change'); expect(restored, isNotNull); expect(restored!['id'], equals('test')); expect(restored['value'], equals(123)); }); test('should restore from latest checkpoint', () { final manager = createCheckpointManager(); manager.createCheckpoint('v1', {'version': 1}, 'First version'); manager.createCheckpoint('v2', {'version': 2}, 'Second version'); manager.createCheckpoint('v3', {'version': 3}, 'Third version'); final restored = manager.restoreFromLatest(); expect(restored, isNotNull); expect(restored!['version'], equals(3)); }); test('should limit checkpoint count', () { final manager = createCheckpointManager(maxCheckpoints: 3); for (int i = 0; i < 5; i++) { manager.createCheckpoint('v$i', {'version': i}, 'Version $i'); } expect(manager.checkpoints.length, equals(3)); expect(manager.checkpoints[0].name, equals('v2')); expect(manager.checkpoints[2].name, equals('v4')); }); }); // ============================================================================ // 性能测试 // ============================================================================ group('Performance', () { test('should serialize 100 components quickly', () { final design = { 'id': 'perf-test', 'components': List.generate(100, (i) => { 'id': 'comp-$i', 'name': 'R$i', 'type': 'resistor', 'value': '${1000 + i}Ω', 'position': {'x': i * 1000, 'y': i * 1000}, }), }; final sw = Stopwatch()..start(); final bytes = designToTile(design); sw.stop(); print('Serialize 100 components: ${sw.elapsedMilliseconds}ms'); expect(sw.elapsedMilliseconds, lessThan(100)); sw.reset(); sw.start(); final restored = tileToDesign(bytes); sw.stop(); print('Deserialize 100 components: ${sw.elapsedMilliseconds}ms'); expect(sw.elapsedMilliseconds, lessThan(100)); }); test('should compress large design', () { final design = { 'id': 'large-test', 'components': List.generate(500, (i) => { 'id': 'comp-$i', 'name': 'C$i', 'type': 'capacitor', 'value': '100nF', 'footprint': '0805', }), }; final jsonSize = design.toString().length; final bytes = designToTile(design); final tileSize = bytes.length; final ratio = tileSize / jsonSize * 100; print('Compression: JSON=$jsonSize bytes, Tile=$tileSize bytes, ratio=${ratio.toStringAsFixed(1)}%'); expect(ratio, lessThan(50)); // 应该压缩到 50% 以下 }); }); }