blob: 51919d8fb11bd7fb12a1a8b1c38d1050596dbcb5 [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.
/// This script generates a test package with a specified number of libraries,
/// classes, methods, and doc comment references, in order to test analyzer's
/// performance, scalability, and stability characteristics.
///
/// Call with `--help` to see all of the args.
library generate_testing_package;
import 'dart:io';
import 'dart:math';
import 'package:args/args.dart';
import 'package:test_descriptor/test_descriptor.dart' as d;
void main(List<String> args) async {
// TODO(srawlins): Support multiple packages which depend on each other, in a
// DAG similar to the import graph.
var argParser = ArgParser()
..addOption(
'library-count',
defaultsTo: '1',
help: 'the number of libraries',
)
..addOption(
'class-count',
defaultsTo: '1',
help: 'the number of classes per library',
)
..addOption(
'method-count',
defaultsTo: '1',
help: 'the number of methods per class',
)
..addOption(
'parameter-count',
defaultsTo: '1',
help: 'the number of parameters per method',
)
..addFlag('use-barrel-file', help: 'Whether to add a barrel import')
..addFlag('use-json-serializable',
help: 'Whether to declare @JsonSerializable classes');
var argResults = argParser.parse(args);
var libraryCount = int.parse(argResults['library-count'] as String);
var classCount = int.parse(argResults['class-count'] as String);
var methodCount = int.parse(argResults['method-count'] as String);
var parameterCount = int.parse(argResults['parameter-count'] as String);
var useBarrelFile = argResults['use-barrel-file'] as bool;
var useJsonSerializable = argResults['use-json-serializable'] as bool;
var testDataDir = Directory('test_data')..createSync();
var libFiles = <d.Descriptor>[];
var classCounter = 1;
var methodCounter = 1;
var middleImportIndex = libraryCount ~/ 2;
// We need a global index for the names of top-level variables, to avoid
// ambiguous elements from imports.
var topLevelVariableIndex = 1;
for (var lIndex = 1; lIndex <= libraryCount; lIndex++) {
var libraryName = 'lib$lIndex'.padLeft(3, '0');
// Each library has an index, starting at 1. The libraries depend on each
// other in tiers. The "tier" of a library is the base-2 logarithm of its
// index. Libraries in higher tiers depend on libraries in lower tiers.
// Libraries in higher tiers depend on more libraries than those in lower
// tiers.
//
// In a package with 10 libraries, we have the following tiers and
// dependencies:
// * T0: lib1 - no imports
// * T1: lib2 - imports lib1
// * T2: lib3, lib4 - each imports two libraries from T0, T1
// * T3: lib5, lib6, lib7, lib8 - each imports three libraries from T0, T1,
// and T2
// * T4: lib9, lib10 - each imports four libraries from T0, T1, T2, T3
//
// In a package with 1000 libraries, there are 11 tiers, and libraries in
// the last have 10 imports each.
// TODO(srawlins): Make the "connectedness" of the import graph
// configurable.
var importGraphTier = (log(lIndex) / ln2).ceil();
var importIndexStep =
importGraphTier == 0 ? -1 : (lIndex - 1) ~/ importGraphTier;
var content = StringBuffer();
if (useJsonSerializable) {
content.writeln("import 'package:json_annotation/json_annotation.dart';");
}
// Add imports in a library above tier 0.
if (importGraphTier > 0) {
if (useBarrelFile && lIndex > middleImportIndex) {
content.writeln(import(testPackageLibUri('barrel.dart')));
}
for (var tierIndex = 1; tierIndex <= importGraphTier; tierIndex++) {
var importIndex = tierIndex * importIndexStep;
if (useBarrelFile) {
if (lIndex <= middleImportIndex || importIndex > middleImportIndex) {
content.writeln(import(testPackageLibUri('lib$importIndex.dart')));
}
} else {
content.writeln(import(testPackageLibUri('lib$importIndex.dart')));
}
}
content.writeln();
}
if (useJsonSerializable) {
content.writeln("part '$libraryName.g.dart';");
content.writeln();
}
// Add top-level variables above tier 0.
if (importGraphTier > 0) {
for (var tierIndex = 1; tierIndex <= importGraphTier; tierIndex++) {
var importIndex = tierIndex * importIndexStep;
// We instantiate a class which is guaranteed to be found in
// `lib$importIndex.dart`.
var classReferenceIndex = classCount * importIndex;
content
.writeln('var x$topLevelVariableIndex = C$classReferenceIndex();');
topLevelVariableIndex++;
}
content.writeln();
}
for (var cIndex = 1; cIndex <= classCount; cIndex++) {
content.writeln('/// Doc comment.');
if (useJsonSerializable) {
content.writeln('@JsonSerializable()');
}
content.writeln('class C$classCounter {');
if (useJsonSerializable) {
content.writeln(' C$classCounter();');
content.writeln(
' factory C$classCounter.fromJson(Map<String, dynamic> json) => '
'_\$C${classCounter}FromJson(json);');
content.writeln(' Map<String, dynamic> toJson() => '
'_\$C${classCounter}ToJson(this);');
}
for (var mIndex = 1; mIndex <= methodCount; mIndex++) {
content.write(' void m$methodCounter(');
content.write(List.generate(parameterCount, (pIndex) => 'int p$pIndex')
.join(', '));
content.writeln(') {}');
methodCounter++;
}
content.writeln('}');
classCounter++;
}
libFiles.add(d.file('$libraryName.dart', content.toString()));
}
if (useBarrelFile) {
// Write the barrel file, which exports the first half of the libraries.
var content = StringBuffer();
for (var j = 1; j <= middleImportIndex; j++) {
content.writeln(export(testPackageLibUri('lib$j.dart')));
}
libFiles.add(d.file('barrel.dart', content.toString()));
}
var testPackageDir = d.dir('test_package', [
d.file('pubspec.yaml', pubspec(useJsonSerializable: useJsonSerializable)),
d.dir('lib', libFiles),
]);
await testPackageDir.create(testDataDir.path);
}
String export(String uri) => "export '$uri';";
String import(String uri) => "import '$uri';";
/// Returns the text of a 'pubspec.yaml' file.
///
/// With [useJsonSerializable], several packages are added to the dependencies
/// in order to test using build_runner.
String pubspec({required bool useJsonSerializable}) {
var dependencies = useJsonSerializable
? '''
dependencies:
json_annotation: any
json_serializable: any
'''
: '';
var devDependencies = useJsonSerializable
? '''
dev_dependencies:
build_runner: any
'''
: '';
return '''
name: test_package
version: 0.0.1
environment:
sdk: '>=2.12.0 <3.0.0'
$dependencies
$devDependencies
''';
}
String testPackageLibUri(String path) => 'package:test_package/$path';