Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit a6dd19e

Browse files
committed
Make change testable
1 parent b07c546 commit a6dd19e

File tree

2 files changed

+84
-14
lines changed

2 files changed

+84
-14
lines changed

packages/path_provider/path_provider_windows/lib/src/path_provider_windows_real.dart

+35-9
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,41 @@ import 'package:win32/win32.dart';
1313

1414
import 'folders.dart';
1515

16+
/// Constant for en-US language used in VersionInfo keys
17+
@visibleForTesting
18+
const String languageEn = '0409';
19+
20+
/// Constant for CP1252 encoding used in VersionInfo keys
21+
@visibleForTesting
22+
const String encodingCP1252 = '04e4';
23+
24+
/// Constant for Unicode encoding used in VersionInfo keys
25+
@visibleForTesting
26+
const String encodingUnicode = '04b0';
27+
1628
/// Wraps the Win32 VerQueryValue API call.
1729
///
1830
/// This class exists to allow injecting alternate metadata in tests without
1931
/// building multiple custom test binaries.
2032
@visibleForTesting
2133
class VersionInfoQuerier {
22-
/// Returns the value for [key] in [versionInfo]s English strings section, or
23-
/// null if there is no such entry, or if versionInfo is null.
24-
String? getStringValue(Pointer<Uint8>? versionInfo, String key) {
34+
/// Returns the value for [key] in [versionInfo]s in section with given
35+
/// language and encoding, or null if there is no such entry,
36+
/// or if versionInfo is null.
37+
///
38+
/// See https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource
39+
/// for list of possible language and encoding values.
40+
String? getStringValue(
41+
Pointer<Uint8>? versionInfo,
42+
String key, {
43+
required String language,
44+
required String encoding,
45+
}) {
2546
if (versionInfo == null) {
2647
return null;
2748
}
28-
const String kEnUsLanguageCode = '040904e4';
2949
final Pointer<Utf16> keyPath =
30-
TEXT('\\StringFileInfo\\$kEnUsLanguageCode\\$key');
50+
TEXT('\\StringFileInfo\\$language$encoding\\$key');
3151
final Pointer<Uint32> length = calloc<Uint32>();
3252
final Pointer<Pointer<Utf16>> valueAddress = calloc<Pointer<Utf16>>();
3353
try {
@@ -150,6 +170,12 @@ class PathProviderWindows extends PathProviderPlatform {
150170
}
151171
}
152172

173+
String? _getStringValue(Pointer<Uint8>? infoBuffer, String key) =>
174+
versionInfoQuerier.getStringValue(infoBuffer, key,
175+
language: languageEn, encoding: encodingCP1252) ??
176+
versionInfoQuerier.getStringValue(infoBuffer, key,
177+
language: languageEn, encoding: encodingUnicode);
178+
153179
/// Returns the relative path string to append to the root directory returned
154180
/// by Win32 APIs for application storage (such as RoamingAppDir) to get a
155181
/// directory that is unique to the application.
@@ -187,10 +213,10 @@ class PathProviderWindows extends PathProviderPlatform {
187213
infoBuffer = null;
188214
}
189215
}
190-
companyName = _sanitizedDirectoryName(
191-
versionInfoQuerier.getStringValue(infoBuffer, 'CompanyName'));
192-
productName = _sanitizedDirectoryName(
193-
versionInfoQuerier.getStringValue(infoBuffer, 'ProductName'));
216+
companyName =
217+
_sanitizedDirectoryName(_getStringValue(infoBuffer, 'CompanyName'));
218+
productName =
219+
_sanitizedDirectoryName(_getStringValue(infoBuffer, 'ProductName'));
194220

195221
// If there was no product name, use the executable name.
196222
productName ??=

packages/path_provider/path_provider_windows/test/path_provider_windows_test.dart

+49-5
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,30 @@ import 'dart:io';
77
import 'package:flutter_test/flutter_test.dart';
88
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
99
import 'package:path_provider_windows/path_provider_windows.dart';
10+
import 'package:path_provider_windows/src/path_provider_windows_real.dart'
11+
show languageEn, encodingCP1252, encodingUnicode;
1012

1113
// A fake VersionInfoQuerier that just returns preset responses.
1214
class FakeVersionInfoQuerier implements VersionInfoQuerier {
13-
FakeVersionInfoQuerier(this.responses);
15+
FakeVersionInfoQuerier(this.responses,
16+
[this.language = languageEn, this.encoding = encodingUnicode]);
1417

18+
final String language;
19+
final String encoding;
1520
final Map<String, String> responses;
1621

17-
String? getStringValue(Pointer<Uint8>? versionInfo, String key) =>
18-
responses[key];
22+
String? getStringValue(
23+
Pointer<Uint8>? versionInfo,
24+
String key, {
25+
required String language,
26+
required String encoding,
27+
}) {
28+
if (language == this.language && encoding == this.encoding) {
29+
return responses[key];
30+
} else {
31+
return null;
32+
}
33+
}
1934
}
2035

2136
void main() {
@@ -40,12 +55,12 @@ void main() {
4055
expect(path, endsWith(r'flutter_tester'));
4156
}, skip: !Platform.isWindows);
4257

43-
test('getApplicationSupportPath with full version info', () async {
58+
test('getApplicationSupportPath with full version info in CP1252', () async {
4459
final PathProviderWindows pathProvider = PathProviderWindows();
4560
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
4661
'CompanyName': 'A Company',
4762
'ProductName': 'Amazing App',
48-
});
63+
}, languageEn, encodingCP1252);
4964
final String? path = await pathProvider.getApplicationSupportPath();
5065
expect(path, isNotNull);
5166
if (path != null) {
@@ -54,6 +69,35 @@ void main() {
5469
}
5570
}, skip: !Platform.isWindows);
5671

72+
test('getApplicationSupportPath with full version info in Unicode', () async {
73+
final PathProviderWindows pathProvider = PathProviderWindows();
74+
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
75+
'CompanyName': 'A Company',
76+
'ProductName': 'Amazing App',
77+
}, languageEn, encodingUnicode);
78+
final String? path = await pathProvider.getApplicationSupportPath();
79+
expect(path, isNotNull);
80+
if (path != null) {
81+
expect(path, endsWith(r'AppData\Roaming\A Company\Amazing App'));
82+
expect(Directory(path).existsSync(), isTrue);
83+
}
84+
}, skip: !Platform.isWindows);
85+
86+
test(
87+
'getApplicationSupportPath with full version info in Unsupported Encoding',
88+
() async {
89+
final PathProviderWindows pathProvider = PathProviderWindows();
90+
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{
91+
'CompanyName': 'A Company',
92+
'ProductName': 'Amazing App',
93+
}, '0000', '0000');
94+
final String? path = await pathProvider.getApplicationSupportPath();
95+
expect(path, contains(r'C:\'));
96+
expect(path, contains(r'AppData'));
97+
// The last path component should be the executable name.
98+
expect(path, endsWith(r'flutter_tester'));
99+
}, skip: !Platform.isWindows);
100+
57101
test('getApplicationSupportPath with missing company', () async {
58102
final PathProviderWindows pathProvider = PathProviderWindows();
59103
pathProvider.versionInfoQuerier = FakeVersionInfoQuerier(<String, String>{

0 commit comments

Comments
 (0)