| import 'dart:async'; |
| import 'dart:io'; |
| |
| import 'package:analyzer_utilities/tools.dart'; |
| import 'package:package_config/package_config.dart'; |
| import 'package:path/path.dart'; |
| import 'package:pub_semver/pub_semver.dart'; |
| import 'package:test/test.dart'; |
| |
| Future<String> _formatText(String text, {required String pkgPath}) async { |
| var packageConfig = await findPackageConfig(Directory(pkgPath)); |
| if (packageConfig == null) { |
| throw StateError( |
| 'Could not find the shared Dart SDK package_config.json file, for ' |
| '"$pkgPath"', |
| ); |
| } |
| var package = packageConfig.packageOf( |
| Uri.file(join(pkgPath, 'pubspec.yaml')), |
| ); |
| if (package == null) { |
| throw StateError('Could not find the package for "$pkgPath"'); |
| } |
| var languageVersion = package.languageVersion; |
| if (languageVersion == null) { |
| throw StateError('Could not find a Dart language version for "$pkgPath"'); |
| } |
| var version = Version(languageVersion.major, languageVersion.minor, 0); |
| return DartFormat.formatString(text, languageVersion: version); |
| } |
| |
| extension GeneratedContentExtension on GeneratedContent { |
| /// Check whether the [output] has the correct contents, and return true if it |
| /// does. |
| /// |
| /// [pkgRoot] is the path to the SDK's `pkg` directory. |
| Future<bool> check(String pkgRoot) async { |
| switch (this) { |
| case GeneratedDirectory self: |
| var outputDirectory = self.output(pkgRoot); |
| var map = self.directoryContentsComputer(pkgRoot); |
| try { |
| for (var entry in map.entries) { |
| var file = entry.key; |
| var fileContentsComputer = entry.value; |
| var expectedContents = await fileContentsComputer(pkgRoot); |
| var outputFile = File(posix.join(outputDirectory.path, file)); |
| var actualContents = outputFile.readAsStringSync(); |
| // Normalize Windows line endings to Unix line endings so that the |
| // comparison doesn't fail on Windows. |
| actualContents = actualContents.replaceAll('\r\n', '\n'); |
| if (expectedContents != actualContents) { |
| return false; |
| } |
| } |
| var nonHiddenFileCount = 0; |
| outputDirectory |
| .listSync(recursive: false, followLinks: false) |
| .forEach((FileSystemEntity fileSystemEntity) { |
| if (fileSystemEntity is File && |
| !basename(fileSystemEntity.path).startsWith('.')) { |
| nonHiddenFileCount++; |
| } |
| }); |
| if (nonHiddenFileCount != map.length) { |
| // The number of files generated doesn't match the number we expected to |
| // generate. |
| return false; |
| } |
| } catch (e) { |
| // There was a problem reading the file (most likely because it didn't |
| // exist). Treat that the same as if the file doesn't have the expected |
| // contents. |
| return false; |
| } |
| return true; |
| case GeneratedFile self: |
| var outputFile = self.output(pkgRoot); |
| var expectedContents = await self.computeContents(pkgRoot); |
| if (self.isDartFile) { |
| expectedContents = await _formatText( |
| expectedContents, |
| pkgPath: dirname(outputFile.path), |
| ); |
| } |
| try { |
| var actualContents = outputFile.readAsStringSync(); |
| // Normalize Windows line endings to Unix line endings so that the |
| // comparison doesn't fail on Windows. |
| actualContents = actualContents.replaceAll('\r\n', '\n'); |
| expectedContents = expectedContents.replaceAll('\r\n', '\n'); |
| return expectedContents == actualContents; |
| } catch (e) { |
| // There was a problem reading the file (most likely because it didn't |
| // exist). Treat that the same as if the file doesn't have the expected |
| // contents. |
| return false; |
| } |
| } |
| } |
| } |
| |
| extension GeneratedContentIterable on Iterable<GeneratedContent> { |
| /// Check that all of the targets in `this` are up to date. If they are not, |
| /// print out a message instructing the user to regenerate them, and exit with |
| /// a nonzero error code. |
| /// |
| /// [pkgRoot] is the path to the SDK's `pkg` directory. [generatorPath] is |
| /// the path to a .dart script the user may use to regenerate the targets. |
| /// |
| /// To avoid mistakes when run on Windows, [generatorPath] always uses |
| /// POSIX directory separators. |
| Future<void> check(String pkgRoot, String generatorPath) async { |
| var generateNeeded = false; |
| for (var target in this) { |
| var ok = await target.check(pkgRoot); |
| if (!ok) { |
| print( |
| '${normalize(target.output(pkgRoot).absolute.path)}' |
| " doesn't have expected contents.", |
| ); |
| generateNeeded = true; |
| } |
| } |
| if (generateNeeded) { |
| print('Please regenerate using:'); |
| var executable = Platform.executable; |
| var generateScript = normalize(joinAll(posix.split(generatorPath))); |
| print(' $executable $generateScript'); |
| fail('Generated content needs to be regenerated'); |
| } |
| } |
| } |