Skip to content

Commit

Permalink
Fixes #293 and implements alternative to PR #347
Browse files Browse the repository at this point in the history
Specific to the Windows platform, which contains a drive letter in absolute paths, the functions Task.split and Task.baseDirectoryPath
did not return the correct values when the baseDirectory was set to baseDirectory.root.
  • Loading branch information
781flyingdutchman committed Sep 2, 2024
1 parent 3e0f855 commit dbd719d
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 86 deletions.
159 changes: 89 additions & 70 deletions example/integration_test/downloader_integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1434,53 +1434,51 @@ void main() {
expect(result['json'], equals({'field1': 1}));
});

testWidgets('post DownloadTask with post is Map',
(widgetTester) async {
final task = DownloadTask(
url: postTestUrl,
urlQueryParameters: {'request-type': 'post-json'},
filename: postFilename,
headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
},
post: {"field1": 1});
final path =
testWidgets('post DownloadTask with post is Map', (widgetTester) async {
final task = DownloadTask(
url: postTestUrl,
urlQueryParameters: {'request-type': 'post-json'},
filename: postFilename,
headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
},
post: {"field1": 1});
final path =
join((await getApplicationDocumentsDirectory()).path, task.filename);
expect((await FileDownloader().download(task)).status,
equals(TaskStatus.complete));
final result = jsonDecode(await File(path).readAsString());
print(result);
expect(result['args']['request-type'], equals('post-json'));
expect(result['headers']['Header1'], equals('headerValue1'));
expect(result['data'], equals('{"field1":1}'));
// confirm the server side interpreted this as JSON
expect(result['json'], equals({'field1': 1}));
});

testWidgets('post DownloadTask with post is List',
(widgetTester) async {
final task = DownloadTask(
url: postTestUrl,
urlQueryParameters: {'request-type': 'post-json'},
filename: postFilename,
headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
},
post: ['apple', 'orange']);
final path =
expect((await FileDownloader().download(task)).status,
equals(TaskStatus.complete));
final result = jsonDecode(await File(path).readAsString());
print(result);
expect(result['args']['request-type'], equals('post-json'));
expect(result['headers']['Header1'], equals('headerValue1'));
expect(result['data'], equals('{"field1":1}'));
// confirm the server side interpreted this as JSON
expect(result['json'], equals({'field1': 1}));
});

testWidgets('post DownloadTask with post is List', (widgetTester) async {
final task = DownloadTask(
url: postTestUrl,
urlQueryParameters: {'request-type': 'post-json'},
filename: postFilename,
headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
},
post: ['apple', 'orange']);
final path =
join((await getApplicationDocumentsDirectory()).path, task.filename);
expect((await FileDownloader().download(task)).status,
equals(TaskStatus.complete));
final result = jsonDecode(await File(path).readAsString());
print(result);
expect(result['args']['request-type'], equals('post-json'));
expect(result['headers']['Header1'], equals('headerValue1'));
expect(result['data'], equals('["apple","orange"]'));
// confirm the server side interpreted this as JSON
expect(result['json'], equals(['apple', 'orange']));
});
expect((await FileDownloader().download(task)).status,
equals(TaskStatus.complete));
final result = jsonDecode(await File(path).readAsString());
print(result);
expect(result['args']['request-type'], equals('post-json'));
expect(result['headers']['Header1'], equals('headerValue1'));
expect(result['data'], equals('["apple","orange"]'));
// confirm the server side interpreted this as JSON
expect(result['json'], equals(['apple', 'orange']));
});

testWidgets('post DownloadTask with post is invalid type',
(widgetTester) async {
Expand Down Expand Up @@ -1575,14 +1573,14 @@ void main() {
});

testWidgets('post request with post is Map', (widgetTester) async {
final request = Request(
url: postTestUrl,
urlQueryParameters: {'request-type': 'post-json'},
headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
},
post: {'field': 1});
final request = Request(url: postTestUrl, urlQueryParameters: {
'request-type': 'post-json'
}, headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
}, post: {
'field': 1
});
final response = await FileDownloader().request(request);
expect(response.statusCode, equals(200));
final result = jsonDecode(response.body);
Expand All @@ -1594,14 +1592,15 @@ void main() {
});

testWidgets('post request with post is List', (widgetTester) async {
final request = Request(
url: postTestUrl,
urlQueryParameters: {'request-type': 'post-json'},
headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
},
post: ['apple', 'orange']);
final request = Request(url: postTestUrl, urlQueryParameters: {
'request-type': 'post-json'
}, headers: {
'Header1': 'headerValue1',
'content-type': 'application/json'
}, post: [
'apple',
'orange'
]);
final response = await FileDownloader().request(request);
expect(response.statusCode, equals(200));
final result = jsonDecode(response.body);
Expand Down Expand Up @@ -1718,10 +1717,12 @@ void main() {
taskStatusCallback: statusCallback,
taskProgressCallback: progressCallback);
expect(
await FileDownloader().enqueue(uploadTask.copyWith(
fields: {'field1': 'value1', 'field2': 'check\u2713',
'field3': '{"a":"b"}', 'field4': '["1", "2"]'},
updates: Updates.statusAndProgress)),
await FileDownloader().enqueue(uploadTask.copyWith(fields: {
'field1': 'value1',
'field2': 'check\u2713',
'field3': '{"a":"b"}',
'field4': '["1", "2"]'
}, updates: Updates.statusAndProgress)),
isTrue);
await statusCallbackCompleter.future;
expect(statusCallbackCounter, equals(3));
Expand Down Expand Up @@ -3342,22 +3343,36 @@ void main() {
group('Task functions', () {
test('baseDirectoryPath', () async {
for (final baseDirectoryEnum in BaseDirectory.values) {
final task =
DownloadTask(url: workingUrl, baseDirectory: baseDirectoryEnum);
final subdirName =
(Platform.isWindows && baseDirectoryEnum == BaseDirectory.root)
? 'C:\\'
: '';
final task = DownloadTask(
url: workingUrl,
baseDirectory: baseDirectoryEnum,
directory: subdirName);
final path = await task.filePath();
final dir = dirname(path);
print('For $baseDirectoryEnum the dir is $dir');
expect(await Task.baseDirectoryPath(baseDirectoryEnum), equals(dir));
if (Platform.isWindows && baseDirectoryEnum == BaseDirectory.root) {
expect(dir, equals('C:\\'));
} else {
expect(await Task.baseDirectoryPath(baseDirectoryEnum), equals(dir));
}
}
});

test('split with filePath parameter', () async {
for (final subdirName in ['something', '']) {
for (final baseDirectoryEnum in BaseDirectory.values) {
final modifiedSubdirName =
(Platform.isWindows && baseDirectoryEnum == BaseDirectory.root)
? 'C:\\$subdirName'
: subdirName;
final task = DownloadTask(
url: workingUrl,
baseDirectory: baseDirectoryEnum,
directory: subdirName);
directory: modifiedSubdirName);
final path = await task.filePath();
print('Testing path $path');
final (baseDirectory, directory, filename) =
Expand All @@ -3372,10 +3387,14 @@ void main() {
test('split with File parameter', () async {
for (final subdirName in ['something', '']) {
for (final baseDirectoryEnum in BaseDirectory.values) {
final modifiedSubdirName =
(Platform.isWindows && baseDirectoryEnum == BaseDirectory.root)
? 'C:\\$subdirName'
: subdirName;
final task = DownloadTask(
url: workingUrl,
baseDirectory: baseDirectoryEnum,
directory: subdirName);
directory: modifiedSubdirName);
final path = await task.filePath();
print('Testing path $path');
final (baseDirectory, directory, filename) =
Expand Down
7 changes: 6 additions & 1 deletion example/windows/flutter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake)
# https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")

# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()

# === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")

Expand Down Expand Up @@ -92,7 +97,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG>
${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM
)
add_custom_target(flutter_assemble DEPENDS
Expand Down
5 changes: 5 additions & 0 deletions example/windows/runner/flutter_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ bool FlutterWindow::OnCreate() {
this->Show();
});

// Flutter can complete the first frame before the "show window" callback is
// registered. The following call ensures a frame is pending to ensure the
// window is shown. It is a no-op if the first frame hasn't completed yet.
flutter_controller_->ForceRedraw();

return true;
}

Expand Down
39 changes: 24 additions & 15 deletions lib/src/task.dart
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ sealed class Task extends Request implements Comparable {
}

/// Returns the path to the directory represented by [baseDirectory]
///
/// On Windows, if [baseDirectory] is .root, returns the empty string
/// because the drive letter is required to be included in the directory
/// path
static Future<String> baseDirectoryPath(BaseDirectory baseDirectory) async {
Directory? externalStorageDirectory;
Directory? externalCacheDirectory;
Expand Down Expand Up @@ -391,7 +395,9 @@ sealed class Task extends Request implements Comparable {
(BaseDirectory.applicationLibrary, true) =>
Directory(p.join(externalStorageDirectory!.path, 'Library'))
};
return baseDir.absolute.path;
return (Platform.isWindows && baseDirectory == BaseDirectory.root)
? ''
: baseDir.absolute.path;
}

/// Extract the baseDirectory, directory and filename from
Expand All @@ -412,19 +418,20 @@ sealed class Task extends Request implements Comparable {
// try to match the start of the absoluteDirectory to one of the
// directories represented by the BaseDirectory enum.
// Order matters, as some may be subdirs of others
final testSequence = Platform.isAndroid || Platform.isLinux
? [
BaseDirectory.temporary,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationDocuments
]
: [
BaseDirectory.temporary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationDocuments
];
final testSequence =
Platform.isAndroid || Platform.isLinux || Platform.isWindows
? [
BaseDirectory.temporary,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationDocuments
]
: [
BaseDirectory.temporary,
BaseDirectory.applicationSupport,
BaseDirectory.applicationLibrary,
BaseDirectory.applicationDocuments
];
for (final baseDirectoryEnum in testSequence) {
final baseDirPath = await baseDirectoryPath(baseDirectoryEnum);
final (match, directory) = _contains(baseDirPath, absoluteDirectoryPath);
Expand All @@ -451,7 +458,9 @@ sealed class Task extends Request implements Comparable {
/// [dirPath] should not contain a filename - if it does, it is returned
/// as part of the subdir.
static (bool, String) _contains(String baseDirPath, String dirPath) {
final match = RegExp('^$baseDirPath/?(.*)').firstMatch(dirPath);
final escapedBaseDirPath =
'$baseDirPath${Platform.pathSeparator}?'.replaceAll(r'\', r'\\');
final match = RegExp('^$escapedBaseDirPath(.*)').firstMatch(dirPath);
return (match != null, match?.group(1) ?? '');
}

Expand Down

0 comments on commit dbd719d

Please sign in to comment.