489 lines
15 KiB
Dart
489 lines
15 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:swagger_generator_flutter/core/models.dart';
|
|
import 'package:swagger_generator_flutter/core/performance_parser.dart';
|
|
import 'package:swagger_generator_flutter/core/smart_cache.dart';
|
|
import 'package:swagger_generator_flutter/generators/performance_generator.dart';
|
|
import 'package:test/test.dart';
|
|
|
|
void main() {
|
|
group('Performance Tests', () {
|
|
group('PerformanceParser', () {
|
|
late PerformanceParser parser;
|
|
|
|
setUp(() {
|
|
parser = PerformanceParser(
|
|
config: ParseConfig(
|
|
enableParallelParsing: true,
|
|
enableStreamParsing: false,
|
|
enablePerformanceStats: true,
|
|
maxConcurrency: 4,
|
|
),
|
|
);
|
|
});
|
|
|
|
test('parses document with performance tracking', () async {
|
|
final jsonString = jsonEncode({
|
|
'openapi': '3.0.3',
|
|
'info': {
|
|
'title': 'Test API',
|
|
'version': '1.0.0',
|
|
},
|
|
'paths': {
|
|
'/users': {
|
|
'get': {
|
|
'summary': 'Get users',
|
|
'responses': {
|
|
'200': {
|
|
'description': 'Success',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
final document = await parser.parseDocument(jsonString);
|
|
|
|
expect(document.title, equals('Test API'));
|
|
expect(document.version, equals('1.0.0'));
|
|
expect(document.paths, hasLength(1));
|
|
|
|
final stats = parser.lastStats;
|
|
expect(stats, isNotNull);
|
|
expect(stats!.totalTime.inMicroseconds, greaterThan(0));
|
|
expect(stats.documentSize, equals(jsonString.length));
|
|
expect(stats.pathCount, equals(1));
|
|
});
|
|
|
|
test('handles large documents efficiently', () async {
|
|
// 创建大型文档
|
|
final paths = <String, dynamic>{};
|
|
for (int i = 0; i < 100; i++) {
|
|
paths['/api/v1/resource$i'] = {
|
|
'get': {
|
|
'summary': 'Get resource $i',
|
|
'responses': {
|
|
'200': {'description': 'Success'},
|
|
},
|
|
},
|
|
'post': {
|
|
'summary': 'Create resource $i',
|
|
'responses': {
|
|
'201': {'description': 'Created'},
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
final largeDoc = {
|
|
'openapi': '3.0.3',
|
|
'info': {
|
|
'title': 'Large API',
|
|
'version': '1.0.0',
|
|
},
|
|
'paths': paths,
|
|
};
|
|
|
|
final jsonString = jsonEncode(largeDoc);
|
|
final stopwatch = Stopwatch()..start();
|
|
|
|
final document = await parser.parseDocument(jsonString);
|
|
|
|
stopwatch.stop();
|
|
|
|
expect(document.paths.length, greaterThan(50)); // 应该解析出多个路径
|
|
expect(stopwatch.elapsedMilliseconds, lessThan(5000)); // 应该在5秒内完成
|
|
|
|
final stats = parser.lastStats;
|
|
expect(stats, isNotNull);
|
|
expect(stats!.pathsPerSecond, greaterThan(10)); // 每秒至少处理10个路径
|
|
});
|
|
|
|
test('parallel parsing improves performance', () async {
|
|
final sequentialParser = PerformanceParser(
|
|
config: ParseConfig(
|
|
enableParallelParsing: false,
|
|
enablePerformanceStats: true,
|
|
),
|
|
);
|
|
|
|
final parallelParser = PerformanceParser(
|
|
config: ParseConfig(
|
|
enableParallelParsing: true,
|
|
enablePerformanceStats: true,
|
|
maxConcurrency: 4,
|
|
),
|
|
);
|
|
|
|
// 创建中等大小的文档
|
|
final paths = <String, dynamic>{};
|
|
for (int i = 0; i < 50; i++) {
|
|
paths['/api/v1/resource$i'] = {
|
|
'get': {
|
|
'summary': 'Get resource $i',
|
|
'responses': {
|
|
'200': {'description': 'Success'}
|
|
},
|
|
},
|
|
};
|
|
}
|
|
|
|
final doc = {
|
|
'openapi': '3.0.3',
|
|
'info': {'title': 'Test API', 'version': '1.0.0'},
|
|
'paths': paths,
|
|
};
|
|
|
|
final jsonString = jsonEncode(doc);
|
|
|
|
// 顺序解析
|
|
await sequentialParser.parseDocument(jsonString);
|
|
final sequentialTime = sequentialParser.lastStats!.totalTime;
|
|
|
|
// 并行解析
|
|
await parallelParser.parseDocument(jsonString);
|
|
final parallelTime = parallelParser.lastStats!.totalTime;
|
|
|
|
// 并行解析应该更快(在有足够任务的情况下)
|
|
print('Sequential: ${sequentialTime.inMilliseconds}ms');
|
|
print('Parallel: ${parallelTime.inMilliseconds}ms');
|
|
|
|
// 注意:在小型文档上,并行可能不会更快,因为开销
|
|
expect(parallelTime.inMilliseconds,
|
|
lessThan(sequentialTime.inMilliseconds * 2));
|
|
});
|
|
});
|
|
|
|
group('SmartCache', () {
|
|
late SmartCache<String> cache;
|
|
|
|
setUp(() {
|
|
cache = SmartCache<String>(
|
|
maxSize: 10,
|
|
strategy: CacheStrategy.smart,
|
|
defaultTtl: Duration(seconds: 1),
|
|
);
|
|
});
|
|
|
|
test('basic cache operations work correctly', () {
|
|
cache.put('key1', 'value1');
|
|
expect(cache.get('key1'), equals('value1'));
|
|
expect(cache.containsKey('key1'), isTrue);
|
|
|
|
cache.remove('key1');
|
|
expect(cache.get('key1'), isNull);
|
|
expect(cache.containsKey('key1'), isFalse);
|
|
});
|
|
|
|
test('cache eviction works when full', () {
|
|
// 填满缓存
|
|
for (int i = 0; i < 10; i++) {
|
|
cache.put('key$i', 'value$i');
|
|
}
|
|
|
|
expect(cache.getStats().size, equals(10));
|
|
|
|
// 添加新项应该触发驱逐
|
|
cache.put('key10', 'value10');
|
|
expect(cache.getStats().size, equals(10));
|
|
expect(cache.getStats().evictions, greaterThan(0));
|
|
});
|
|
|
|
test('TTL expiration works', () async {
|
|
cache.put('key1', 'value1', ttl: Duration(milliseconds: 100));
|
|
expect(cache.get('key1'), equals('value1'));
|
|
|
|
await Future.delayed(Duration(milliseconds: 150));
|
|
expect(cache.get('key1'), isNull);
|
|
});
|
|
|
|
test('cache statistics are accurate', () {
|
|
cache.put('key1', 'value1');
|
|
cache.get('key1'); // hit
|
|
cache.get('key2'); // miss
|
|
|
|
final stats = cache.getStats();
|
|
expect(stats.totalRequests, equals(2));
|
|
expect(stats.hits, equals(1));
|
|
expect(stats.misses, equals(1));
|
|
expect(stats.hitRate, equals(0.5));
|
|
});
|
|
|
|
test('smart eviction strategy works', () {
|
|
// 添加一些项并模拟不同的访问模式
|
|
cache.put('frequent', 'value1');
|
|
cache.put('recent', 'value2');
|
|
cache.put('old', 'value3');
|
|
|
|
// 频繁访问第一个
|
|
for (int i = 0; i < 5; i++) {
|
|
cache.get('frequent');
|
|
}
|
|
|
|
// 最近访问第二个
|
|
cache.get('recent');
|
|
|
|
// 第三个很久没访问
|
|
|
|
// 填满缓存以触发驱逐
|
|
for (int i = 0; i < 8; i++) {
|
|
cache.put('filler$i', 'value$i');
|
|
}
|
|
|
|
// 频繁访问的项应该还在
|
|
expect(cache.get('frequent'), equals('value1'));
|
|
|
|
// 旧的项可能被驱逐了
|
|
// expect(cache.get('old'), isNull);
|
|
});
|
|
});
|
|
|
|
group('PerformanceGenerator', () {
|
|
late PerformanceGenerator generator;
|
|
late SwaggerDocument testDocument;
|
|
|
|
setUp(() {
|
|
generator = PerformanceGenerator(
|
|
maxConcurrency: 4,
|
|
enableCaching: true,
|
|
enableIncremental: true,
|
|
enableParallel: true,
|
|
);
|
|
|
|
testDocument = SwaggerDocument(
|
|
title: 'Performance Test API',
|
|
version: '1.0.0',
|
|
description: 'API for performance testing',
|
|
servers: [ApiServer(url: 'https://api.example.com')],
|
|
components: ApiComponents(schemas: {}, securitySchemes: {}),
|
|
paths: {
|
|
'/users': ApiPath(
|
|
path: '/users',
|
|
method: HttpMethod.get,
|
|
summary: 'Get users',
|
|
description: 'Get all users',
|
|
operationId: 'getUsers',
|
|
tags: ['users'],
|
|
parameters: [],
|
|
responses: {
|
|
'200': ApiResponse(
|
|
code: '200',
|
|
description: 'Success',
|
|
),
|
|
},
|
|
),
|
|
'/posts': ApiPath(
|
|
path: '/posts',
|
|
method: HttpMethod.get,
|
|
summary: 'Get posts',
|
|
description: 'Get all posts',
|
|
operationId: 'getPosts',
|
|
tags: ['posts'],
|
|
parameters: [],
|
|
responses: {
|
|
'200': ApiResponse(
|
|
code: '200',
|
|
description: 'Success',
|
|
),
|
|
},
|
|
),
|
|
},
|
|
models: {
|
|
'User': ApiModel(
|
|
name: 'User',
|
|
description: 'User model',
|
|
properties: {
|
|
'id': ApiProperty(
|
|
name: 'id',
|
|
type: PropertyType.integer,
|
|
description: 'User ID',
|
|
required: true,
|
|
),
|
|
'name': ApiProperty(
|
|
name: 'name',
|
|
type: PropertyType.string,
|
|
description: 'User name',
|
|
required: true,
|
|
),
|
|
},
|
|
required: ['id', 'name'],
|
|
),
|
|
},
|
|
controllers: {},
|
|
security: [],
|
|
);
|
|
});
|
|
|
|
test('generates code with performance tracking', () async {
|
|
final result = await generator.generateFromDocument(testDocument);
|
|
|
|
expect(result, isNotEmpty);
|
|
expect(result, contains('Performance Test API'));
|
|
expect(result, contains('class User'));
|
|
expect(result, contains('UsersApi'));
|
|
expect(result, contains('PostsApi'));
|
|
|
|
final stats = generator.getStats();
|
|
expect(stats.totalTasks, greaterThan(0));
|
|
expect(stats.completedTasks, greaterThan(0));
|
|
expect(stats.successRate, equals(1.0));
|
|
});
|
|
|
|
test('caching improves performance on repeated generation', () async {
|
|
// 第一次生成
|
|
final stopwatch1 = Stopwatch()..start();
|
|
await generator.generateFromDocument(testDocument);
|
|
stopwatch1.stop();
|
|
final firstTime = stopwatch1.elapsedMicroseconds;
|
|
|
|
// 第二次生成(应该使用缓存)
|
|
final stopwatch2 = Stopwatch()..start();
|
|
await generator.generateFromDocument(testDocument);
|
|
stopwatch2.stop();
|
|
final secondTime = stopwatch2.elapsedMicroseconds;
|
|
|
|
print('First generation: ${firstTime}μs');
|
|
print('Second generation: ${secondTime}μs');
|
|
|
|
// 第二次应该更快(由于缓存)
|
|
expect(secondTime, lessThan(firstTime));
|
|
|
|
final cacheStats = generator.getCacheStats();
|
|
expect(cacheStats.hits, greaterThan(0));
|
|
});
|
|
|
|
test('parallel generation works with multiple modules', () async {
|
|
// 创建有多个模块的大型文档
|
|
final largePaths = <String, ApiPath>{};
|
|
final modules = ['users', 'posts', 'comments', 'tags', 'categories'];
|
|
|
|
for (final module in modules) {
|
|
for (int i = 0; i < 5; i++) {
|
|
final path = '/api/v1/$module/$i';
|
|
largePaths[path] = ApiPath(
|
|
path: path,
|
|
method: HttpMethod.get,
|
|
summary: 'Get $module $i',
|
|
description: 'Get $module item $i',
|
|
operationId: 'get${module.toUpperCase()}$i',
|
|
tags: [module],
|
|
parameters: [],
|
|
responses: {
|
|
'200': ApiResponse(code: '200', description: 'Success'),
|
|
},
|
|
);
|
|
}
|
|
}
|
|
|
|
final largeDocument = SwaggerDocument(
|
|
title: 'Large API',
|
|
version: '1.0.0',
|
|
description: 'Large API for testing',
|
|
servers: [ApiServer(url: 'https://api.example.com')],
|
|
components: ApiComponents(schemas: {}, securitySchemes: {}),
|
|
paths: largePaths,
|
|
models: {},
|
|
controllers: {},
|
|
security: [],
|
|
);
|
|
|
|
final result = await generator.generateFromDocument(largeDocument);
|
|
|
|
expect(result, isNotEmpty);
|
|
expect(result, contains('Large API'));
|
|
|
|
// 应该包含所有模块的 API
|
|
for (final module in modules) {
|
|
final className =
|
|
'${module[0].toUpperCase()}${module.substring(1)}Api';
|
|
expect(result, contains(className));
|
|
}
|
|
|
|
final stats = generator.getStats();
|
|
expect(stats.totalTasks, greaterThan(modules.length));
|
|
expect(stats.parallelEfficiency, greaterThan(0.5));
|
|
});
|
|
|
|
test('incremental generation detects no changes', () async {
|
|
// 第一次生成
|
|
await generator.generateFromDocument(testDocument);
|
|
final firstStats = generator.getStats();
|
|
|
|
// 第二次生成相同文档(应该检测到无变更)
|
|
await generator.generateFromDocument(testDocument);
|
|
final secondStats = generator.getStats();
|
|
|
|
// 第二次生成的任务数应该更少(由于增量生成)
|
|
expect(
|
|
secondStats.totalTasks, lessThanOrEqualTo(firstStats.totalTasks));
|
|
});
|
|
});
|
|
|
|
group('Real Project Performance', () {
|
|
test('handles real swagger.json efficiently', () async {
|
|
final file = File('swagger.json');
|
|
if (!file.existsSync()) {
|
|
print(
|
|
'swagger.json not found, skipping real project performance test');
|
|
return;
|
|
}
|
|
|
|
final jsonString = await file.readAsString();
|
|
|
|
// 解析性能测试
|
|
final parser = PerformanceParser(
|
|
config: ParseConfig(
|
|
enableParallelParsing: true,
|
|
enablePerformanceStats: true,
|
|
maxConcurrency: 4,
|
|
),
|
|
);
|
|
|
|
final parseStopwatch = Stopwatch()..start();
|
|
final document = await parser.parseDocument(jsonString);
|
|
parseStopwatch.stop();
|
|
|
|
print('Parse Performance:');
|
|
print(' Time: ${parseStopwatch.elapsedMilliseconds}ms');
|
|
print(
|
|
' Document size: ${(jsonString.length / 1024).toStringAsFixed(2)}KB');
|
|
print(' Paths: ${document.paths.length}');
|
|
print(' Models: ${document.models.length}');
|
|
|
|
final parseStats = parser.lastStats!;
|
|
print(parseStats.toString());
|
|
|
|
expect(
|
|
parseStopwatch.elapsedMilliseconds, lessThan(10000)); // 应该在10秒内完成
|
|
expect(parseStats.pathsPerSecond, greaterThan(1)); // 每秒至少处理1个路径
|
|
|
|
// 生成性能测试
|
|
final generator = PerformanceGenerator(
|
|
maxConcurrency: 4,
|
|
enableCaching: true,
|
|
enableParallel: true,
|
|
);
|
|
|
|
final genStopwatch = Stopwatch()..start();
|
|
final result = await generator.generateFromDocument(document);
|
|
genStopwatch.stop();
|
|
|
|
print('\nGeneration Performance:');
|
|
print(' Time: ${genStopwatch.elapsedMilliseconds}ms');
|
|
print(
|
|
' Generated size: ${(result.length / 1024).toStringAsFixed(2)}KB');
|
|
print(' Lines: ${result.split('\n').length}');
|
|
|
|
final genStats = generator.getStats();
|
|
print(genStats.toString());
|
|
|
|
expect(genStopwatch.elapsedMilliseconds, lessThan(15000)); // 应该在15秒内完成
|
|
expect(result.length, greaterThan(1000)); // 应该生成足够的代码
|
|
expect(genStats.successRate, greaterThan(0.8)); // 至少80%的任务成功
|
|
});
|
|
});
|
|
});
|
|
}
|