blob: c9329beaa0b6cc0d674523254644914880a2cd7c [file] [log] [blame]
// 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);
}