| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| import 'dart:convert' show utf8; | |
| import 'dart:io'; | |
| String _basename(String path) { | |
| return path.split(Platform.pathSeparator).last; | |
| } | |
| String _generateDirListing(String dirPath) { | |
| final Directory dir = Directory(dirPath); | |
| final List<FileSystemEntity> entities = dir.listSync(); | |
| entities.sort( | |
| (FileSystemEntity a, FileSystemEntity b) => a.path.compareTo(b.path)); | |
| return entities | |
| .map((FileSystemEntity entity) => _basename(entity.path)) | |
| .join('\n'); | |
| } | |
| String _strReplaceRange( | |
| String inputStr, int start, int end, String replacement) { | |
| return inputStr.substring(0, start) + replacement + inputStr.substring(end); | |
| } | |
| String _redirectPatch(String patch) { | |
| final RegExp inputPathExp = RegExp(r'^--- a(.*)', multiLine: true); | |
| final RegExp outputPathExp = RegExp(r'^\+\+\+ b(.*)', multiLine: true); | |
| final Match? inputPathMatch = inputPathExp.firstMatch(patch); | |
| final Match? outputPathMatch = outputPathExp.firstMatch(patch); | |
| assert(inputPathMatch != null); | |
| assert(outputPathMatch != null); | |
| if (inputPathMatch != null && outputPathMatch != null) { | |
| return _strReplaceRange( | |
| patch, | |
| outputPathMatch.start + 5, // +5 to account for '+++ b' | |
| outputPathMatch.end, | |
| inputPathMatch.group(1)!, | |
| ); | |
| } | |
| throw Exception('Unable to find input and output paths'); | |
| } | |
| File _makeTempFile(String prefix) { | |
| final Directory systemTempDir = Directory.systemTemp; | |
| final String filename = '$prefix-${DateTime.now().millisecondsSinceEpoch}'; | |
| final String path = '${systemTempDir.path}${Platform.pathSeparator}$filename'; | |
| final File result = File(path); | |
| result.createSync(); | |
| return result; | |
| } | |
| /// Run the diff of the contents of a directory at [dirPath] and the contents of | |
| /// a file at [goldenPath]. Returns 0 if there is no diff. Be aware that the | |
| /// CWD should be inside of the git repository for the patch to be correct. | |
| int dirContentsDiff(String goldenPath, String dirPath) { | |
| if (!File(goldenPath).existsSync()) { | |
| throw Exception('unable to find `$goldenPath`'); | |
| } | |
| if (!Directory(dirPath).existsSync()) { | |
| throw Exception('unable to find `$dirPath`'); | |
| } | |
| int result = 0; | |
| final File tempFile = _makeTempFile('dir_contents_diff'); | |
| try { | |
| final String dirListing = _generateDirListing(dirPath); | |
| tempFile.writeAsStringSync(dirListing); | |
| final ProcessResult diffResult = Process.runSync( | |
| 'git', | |
| <String>[ | |
| 'diff', | |
| // If you manually edit the golden file, many text editors will add | |
| // trailing whitespace. This flag ignores that because honestly it's | |
| // not a significant part of this test. | |
| '--ignore-space-at-eol', | |
| '-p', | |
| goldenPath, | |
| tempFile.path, | |
| ], | |
| runInShell: true, | |
| stdoutEncoding: utf8, | |
| ); | |
| if (diffResult.exitCode != 0) { | |
| print( | |
| 'Unexpected diff in $goldenPath, use `git apply` with the following patch.\n'); | |
| print(_redirectPatch(diffResult.stdout as String)); | |
| result = 1; | |
| } | |
| } finally { | |
| tempFile.deleteSync(); | |
| } | |
| return result; | |
| } | |
| /// The main entrypoint for the program, returns `exitCode`. | |
| int run(List<String> args) { | |
| if (args.length != 2) { | |
| throw Exception('usage: <path to golden> <path to directory>'); | |
| } | |
| final String goldenPath = args[0]; | |
| final String dirPath = args[1]; | |
| return dirContentsDiff(goldenPath, dirPath); | |
| } |