Skip to content

Commit 20f7daa

Browse files
authored
Validate files don't differ in case only (#3740)
1 parent ea11b0d commit 20f7daa

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

lib/src/validator.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import 'validator/dependency_override.dart';
1919
import 'validator/deprecated_fields.dart';
2020
import 'validator/directory.dart';
2121
import 'validator/executable.dart';
22+
import 'validator/file_case.dart';
2223
import 'validator/flutter_constraint.dart';
2324
import 'validator/flutter_plugin_format.dart';
2425
import 'validator/gitignore.dart';
@@ -136,6 +137,7 @@ abstract class Validator {
136137
required List<String> errors,
137138
}) async {
138139
var validators = [
140+
FileCaseValidator(),
139141
AnalyzeValidator(),
140142
GitignoreValidator(),
141143
PubspecValidator(),

lib/src/validator/file_case.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:async';
6+
7+
import 'package:collection/collection.dart';
8+
9+
import '../validator.dart';
10+
11+
/// Validates that a package files all are unique even after case-normalization.
12+
class FileCaseValidator extends Validator {
13+
@override
14+
Future validate() async {
15+
final lowerCaseToFile = <String, String>{};
16+
for (final file in files.sorted()) {
17+
final lowerCase = file.toLowerCase();
18+
final existing = lowerCaseToFile[lowerCase];
19+
if (existing != null) {
20+
errors.add('''
21+
The file $file and $existing only differ in capitalization.
22+
23+
This is not supported across platforms.
24+
25+
Try renaming one of them.
26+
''');
27+
break;
28+
}
29+
lowerCaseToFile[lowerCase] = file;
30+
}
31+
}
32+
}

test/validator/file_case_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// These tests only work on case-sensitive file systems (ie. only on linux).
6+
@OnPlatform({
7+
'windows': Skip('Windows file system is case-insensitive'),
8+
'mac-os': Skip('MacOS file system is case-insensitive')
9+
})
10+
11+
import 'package:pub/src/exit_codes.dart';
12+
import 'package:test/test.dart';
13+
14+
import '../descriptor.dart' as d;
15+
import '../test_pub.dart';
16+
17+
Future<void> expectValidation(error, int exitCode) async {
18+
await runPub(
19+
error: error,
20+
args: ['publish', '--dry-run'],
21+
workingDirectory: d.path(appPath),
22+
exitCode: exitCode,
23+
);
24+
}
25+
26+
late d.DirectoryDescriptor fakeFlutterRoot;
27+
28+
void main() {
29+
test('Recognizes files that only differ in capitalization.', () async {
30+
await d.validPackage.create();
31+
await d.dir(appPath, [d.file('Pubspec.yaml')]).create();
32+
await expectValidation(
33+
allOf(
34+
contains('Package validation found the following error:'),
35+
contains(
36+
'The file ./pubspec.yaml and ./Pubspec.yaml only differ in capitalization.',
37+
),
38+
),
39+
DATA,
40+
);
41+
});
42+
}

0 commit comments

Comments
 (0)