Skip to content

Commit add7595

Browse files
authored
TestProfile packages: explicit imported and generated fields. (#8634)
1 parent ba1a201 commit add7595

36 files changed

+217
-138
lines changed

app/lib/tool/test_profile/import_source.dart

Lines changed: 44 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -46,49 +46,21 @@ abstract class ImportSource {
4646
static ImportSource autoGenerated() => _autoGeneratedImportSource;
4747
}
4848

49-
/// Resolves and downloads data from pub.dev.
50-
class _PubDevImportSource implements ImportSource {
51-
final String archiveCachePath;
49+
abstract class _ImportSource implements ImportSource {
5250
final _client = Client();
5351

54-
_PubDevImportSource({
55-
required this.archiveCachePath,
56-
});
57-
58-
@override
59-
Future<List<ResolvedVersion>> resolveVersions(TestProfile profile) =>
60-
resolver.resolveVersions(_client, profile);
61-
62-
@override
63-
Future<List<int>> getArchiveBytes(String package, String version) async {
64-
final archiveName = '$package-$version.tar.gz';
65-
final file = File(p.join(archiveCachePath, archiveName));
66-
// download package archive if not already in the cache
67-
if (!await file.exists()) {
68-
// TODO: Use archive_url from version-listing, that is stable!
69-
final rs = await _client.get(Uri.parse(
70-
'${urls.siteRoot}/packages/$package/versions/$version.tar.gz',
71-
));
72-
await file.parent.create(recursive: true);
73-
await file.writeAsBytes(rs.bodyBytes);
74-
}
75-
return await file.readAsBytes();
76-
}
77-
7852
@override
79-
Future<void> close() async {
80-
_client.close();
53+
Future<List<ResolvedVersion>> resolveVersions(TestProfile profile) async {
54+
return <ResolvedVersion>[
55+
...await resolver.resolveVersions(_client, profile),
56+
...await _resolveGeneratedVersions(profile),
57+
];
8158
}
82-
}
83-
84-
/// Generates data based on random seed, without any network (or file) access.
85-
class _AutoGeneratedImportSource implements ImportSource {
86-
final _archives = <String, List<int>>{};
8759

88-
@override
89-
Future<List<ResolvedVersion>> resolveVersions(TestProfile profile) async {
60+
Future<List<ResolvedVersion>> _resolveGeneratedVersions(
61+
TestProfile profile) async {
9062
final versions = <ResolvedVersion>[];
91-
profile.packages.forEach((p) {
63+
profile.generatedPackages.forEach((p) {
9264
final vs = <String>[
9365
...?p.versions?.map((v) => v.version),
9466
];
@@ -119,6 +91,41 @@ class _AutoGeneratedImportSource implements ImportSource {
11991
return versions;
12092
}
12193

94+
@override
95+
Future<void> close() async {
96+
_client.close();
97+
}
98+
}
99+
100+
/// Resolves and downloads data from pub.dev.
101+
class _PubDevImportSource extends _ImportSource {
102+
final String archiveCachePath;
103+
104+
_PubDevImportSource({
105+
required this.archiveCachePath,
106+
});
107+
108+
@override
109+
Future<List<int>> getArchiveBytes(String package, String version) async {
110+
final archiveName = '$package-$version.tar.gz';
111+
final file = File(p.join(archiveCachePath, archiveName));
112+
// download package archive if not already in the cache
113+
if (!await file.exists()) {
114+
// TODO: Use archive_url from version-listing, that is stable!
115+
final rs = await _client.get(Uri.parse(
116+
'${urls.siteRoot}/packages/$package/versions/$version.tar.gz',
117+
));
118+
await file.parent.create(recursive: true);
119+
await file.writeAsBytes(rs.bodyBytes);
120+
}
121+
return await file.readAsBytes();
122+
}
123+
}
124+
125+
/// Generates data based on random seed, without any network (or file) access.
126+
class _AutoGeneratedImportSource extends _ImportSource {
127+
final _archives = <String, List<int>>{};
128+
122129
@override
123130
Future<List<int>> getArchiveBytes(String package, String version) async {
124131
final key = '$package/$version';
@@ -189,9 +196,6 @@ enum TypeEnum { a, b }
189196
_archives[key] = content;
190197
return content;
191198
}
192-
193-
@override
194-
Future<void> close() async {}
195199
}
196200

197201
@visibleForTesting

app/lib/tool/test_profile/importer.dart

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'dart:io';
99
import 'package:_pub_shared/data/admin_api.dart';
1010
import 'package:_pub_shared/data/package_api.dart';
1111
import 'package:_pub_shared/search/tags.dart';
12+
import 'package:collection/collection.dart';
1213
import 'package:meta/meta.dart';
1314
import 'package:tar/tar.dart';
1415

@@ -37,7 +38,11 @@ Future<void> importProfile({
3738
// expand profile with resolved version information
3839
profile = normalize(profile, resolvedVersions: resolvedVersions);
3940

40-
if (profile.packages
41+
if (profile.importedPackages
42+
.any((p) => p.uploaders != null && p.uploaders!.length > 1)) {
43+
throw UnimplementedError('More than one uploader is not implemented.');
44+
}
45+
if (profile.generatedPackages
4146
.any((p) => p.uploaders != null && p.uploaders!.length > 1)) {
4247
throw UnimplementedError('More than one uploader is not implemented.');
4348
}
@@ -116,7 +121,7 @@ Future<void> importProfile({
116121
'\n$lastException\n$lastStackTrace');
117122
}
118123

119-
for (final testPackage in profile.packages) {
124+
Future<void> updatePackage(TestPackage testPackage) async {
120125
final packageName = testPackage.name;
121126
final activeEmail = lastActiveUploaderEmails[packageName];
122127

@@ -167,6 +172,13 @@ Future<void> importProfile({
167172
}
168173
}
169174

175+
for (final p in profile.importedPackages) {
176+
await updatePackage(p);
177+
}
178+
for (final p in profile.generatedPackages) {
179+
await updatePackage(p);
180+
}
181+
170182
final createLikeCounts = <String, int>{};
171183
// create users
172184
for (final u in profile.users) {
@@ -187,10 +199,10 @@ Future<void> importProfile({
187199
);
188200
}
189201
// fill in missing likes
190-
for (final p in profile.packages) {
202+
Future<void> createMissingLike(TestPackage p) async {
191203
if (p.likeCount != null) {
192204
final likesMissing = p.likeCount! - (createLikeCounts[p.name] ?? 0);
193-
if (likesMissing <= 0) continue;
205+
if (likesMissing <= 0) return;
194206

195207
for (var i = 0; i < likesMissing; i++) {
196208
final userEmail = 'like-$i@pub.dev';
@@ -205,12 +217,21 @@ Future<void> importProfile({
205217
}
206218
}
207219

220+
for (final p in profile.importedPackages) {
221+
await createMissingLike(p);
222+
}
223+
for (final p in profile.generatedPackages) {
224+
await createMissingLike(p);
225+
}
226+
208227
await source.close();
209228
await asyncQueue.ongoingProcessing;
210229
}
211230

212231
List<String> _potentialActiveEmails(TestProfile profile, String packageName) {
213-
final testPackage = profile.packages.firstWhere((p) => p.name == packageName);
232+
final testPackage =
233+
profile.importedPackages.firstWhereOrNull((p) => p.name == packageName) ??
234+
profile.generatedPackages.firstWhere((p) => p.name == packageName);
214235

215236
// uploaders
216237
if (testPackage.publisher == null) return testPackage.uploaders!;

app/lib/tool/test_profile/models.dart

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,33 @@ import 'package:yaml/yaml.dart' as yaml;
99

1010
part 'models.g.dart';
1111

12-
/// The configuration to use when creating a local (partial) mirror of pub.dev
13-
/// in order to us it in tests.
12+
/// The configuration to use when creating a local database for testing and
13+
/// development, containing a partial mirror of pub.dev and also synthetic
14+
/// generated package contents.
1415
@JsonSerializable(explicitToJson: true, includeIfNull: false)
1516
class TestProfile {
16-
final List<TestPackage> packages;
17+
/// Packages that will be imported from pub.dev.
18+
///
19+
/// The archive of the package will be used without modification.
20+
/// The package options may be updated as provided by the [TestPackage].
21+
final List<TestPackage> importedPackages;
22+
23+
/// Packages that will be generated locally using the provided parameters and semi-random templates.
24+
final List<TestPackage> generatedPackages;
1725
final List<TestPublisher> publishers;
1826
final List<TestUser> users;
1927

2028
/// The email address of the default user.
2129
final String? defaultUser;
2230

2331
TestProfile({
24-
required List<TestPackage>? packages,
32+
List<TestPackage>? importedPackages,
33+
List<TestPackage>? generatedPackages,
2534
List<TestPublisher>? publishers,
2635
List<TestUser>? users,
2736
this.defaultUser,
28-
}) : packages = packages ?? <TestPackage>[],
37+
}) : importedPackages = importedPackages ?? <TestPackage>[],
38+
generatedPackages = generatedPackages ?? <TestPackage>[],
2939
publishers = publishers ?? <TestPublisher>[],
3040
users = users ?? <TestUser>[];
3141

@@ -41,7 +51,8 @@ class TestProfile {
4151

4252
TestProfile changeDefaultUser(String email) {
4353
return TestProfile(
44-
packages: packages,
54+
importedPackages: importedPackages,
55+
generatedPackages: generatedPackages,
4556
publishers: publishers,
4657
users: users,
4758
defaultUser: email,

app/lib/tool/test_profile/models.g.dart

Lines changed: 8 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/lib/tool/test_profile/normalizer.dart

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ TestProfile normalize(
1212
}) {
1313
final users = <String, TestUser>{};
1414
final publishers = <String, TestPublisher>{};
15-
final packages = <String, TestPackage>{};
15+
final importedPackages = <String, TestPackage>{};
16+
final generatedPackages = <String, TestPackage>{};
1617

1718
profile.users.forEach((user) {
1819
users[user.email] = user;
@@ -28,8 +29,14 @@ TestProfile normalize(
2829
});
2930
});
3031

31-
profile.packages.forEach((package) {
32-
packages[package.name] = package;
32+
profile.importedPackages.forEach((package) {
33+
importedPackages[package.name] = package;
34+
package.uploaders?.forEach((uploader) {
35+
_createUserIfNeeded(users, uploader);
36+
});
37+
});
38+
profile.generatedPackages.forEach((package) {
39+
generatedPackages[package.name] = package;
3340
package.uploaders?.forEach((uploader) {
3441
_createUserIfNeeded(users, uploader);
3542
});
@@ -40,7 +47,10 @@ TestProfile normalize(
4047
// add missing packages from resolved versions
4148
if (resolvedVersions != null) {
4249
resolvedVersions.forEach((rv) {
43-
packages.putIfAbsent(
50+
if (generatedPackages.containsKey(rv.package)) {
51+
return;
52+
}
53+
importedPackages.putIfAbsent(
4454
rv.package,
4555
() => TestPackage(
4656
name: rv.package,
@@ -51,8 +61,8 @@ TestProfile normalize(
5161
));
5262
});
5363
// update versions from resolved versions
54-
packages.values.toList().forEach((p) {
55-
final versions = resolvedVersions
64+
List<TestVersion> getUpdateVersions(TestPackage p) {
65+
return resolvedVersions
5666
.where((rv) => rv.package == p.name)
5767
.map((rv) => rv.version)
5868
.toSet()
@@ -61,27 +71,49 @@ TestProfile normalize(
6171
created:
6272
resolvedVersions.firstWhere((x) => x.version == v).created))
6373
.toList();
64-
packages[p.name] = p.change(versions: versions);
74+
}
75+
76+
importedPackages.values.toList().forEach((p) {
77+
importedPackages[p.name] = p.change(versions: getUpdateVersions(p));
78+
});
79+
generatedPackages.values.toList().forEach((p) {
80+
generatedPackages[p.name] = p.change(versions: getUpdateVersions(p));
6581
});
6682
}
6783

68-
packages.values.toList().forEach((package) {
69-
if (package.publisher != null) {
70-
_createPublisherIfNeeded(
71-
publishers,
72-
package.publisher!,
73-
memberEmail: defaultUser,
74-
);
75-
} else if (package.uploaders == null || package.uploaders!.isEmpty) {
76-
packages[package.name] = package.change(uploaders: [defaultUser]);
84+
final publishersToCreate = {
85+
...importedPackages.values.map((p) => p.publisher).nonNulls,
86+
...generatedPackages.values.map((p) => p.publisher).nonNulls,
87+
};
88+
for (final publisher in publishersToCreate) {
89+
_createPublisherIfNeeded(
90+
publishers,
91+
publisher,
92+
memberEmail: defaultUser,
93+
);
94+
}
95+
96+
for (final package in importedPackages.values.toList()) {
97+
if (package.publisher == null &&
98+
(package.uploaders == null || package.uploaders!.isEmpty)) {
99+
importedPackages[package.name] = package.change(uploaders: [defaultUser]);
77100
}
78-
});
101+
}
102+
103+
for (final package in generatedPackages.values.toList()) {
104+
if (package.publisher == null &&
105+
(package.uploaders == null || package.uploaders!.isEmpty)) {
106+
generatedPackages[package.name] =
107+
package.change(uploaders: [defaultUser]);
108+
}
109+
}
79110

80111
return TestProfile(
81112
defaultUser: defaultUser,
82113
users: users.values.toList(),
83114
publishers: publishers.values.toList(),
84-
packages: packages.values.toList(),
115+
importedPackages: importedPackages.values.toList(),
116+
generatedPackages: generatedPackages.values.toList(),
85117
);
86118
}
87119

app/lib/tool/test_profile/resolver.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import 'models.dart';
2424
/// than the profile originally specified).
2525
Future<List<ResolvedVersion>> resolveVersions(
2626
http.Client client, TestProfile profile) async {
27+
if (profile.importedPackages.isEmpty) {
28+
return [];
29+
}
2730
return await withTempDirectory((temp) async {
2831
final pubCacheDir = Directory(p.join(temp.path, 'pub-cache'));
2932
await pubCacheDir.create();
@@ -40,7 +43,7 @@ Future<List<ResolvedVersion>> resolveVersions(
4043
pubCacheDir: pubCacheDir.path,
4144
);
4245

43-
for (final package in profile.packages) {
46+
for (final package in profile.importedPackages) {
4447
final versions = package.versions == null || package.versions!.isEmpty
4548
? <TestVersion>[TestVersion(version: 'any', created: null)]
4649
: package.versions;

0 commit comments

Comments
 (0)