diff --git a/dio/test/download_test.dart b/dio/test/download_test.dart deleted file mode 100644 index 6027b3942..000000000 --- a/dio/test/download_test.dart +++ /dev/null @@ -1,231 +0,0 @@ -@TestOn('vm') -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:dio_test/util.dart'; -import 'package:path/path.dart' as p; -import 'package:test/test.dart'; - -import 'mock/adapters.dart'; -import 'utils.dart'; - -void main() { - late Directory tmp; - - setUpAll(() { - tmp = Directory.systemTemp.createTempSync('dio_test_'); - addTearDown(() { - tmp.deleteSync(recursive: true); - }); - }); - - setUp(startServer); - tearDown(stopServer); - - test('download does not change the response type', () async { - final savePath = p.join(tmp.path, 'download0.md'); - - final dio = Dio()..options.baseUrl = serverUrl.toString(); - final options = Options(responseType: ResponseType.plain); - await dio.download('/download', savePath, options: options); - expect(options.responseType, ResponseType.plain); - }); - - test('download1', () async { - final savePath = p.join(tmp.path, 'download1.md'); - final dio = Dio()..options.baseUrl = serverUrl.toString(); - await dio.download('/download', savePath); - - int? total; - int? count; - await dio.download( - '/download', - savePath, - onReceiveProgress: (c, t) { - total = t; - count = c; - }, - ); - - final f = File(savePath); - expect(f.readAsStringSync(), equals('I am a text file')); - expect(count, f.readAsBytesSync().length); - expect(count, total); - }); - - test('download2', () async { - final savePath = p.join(tmp.path, 'download2.md'); - final dio = Dio()..options.baseUrl = serverUrl.toString(); - await dio.downloadUri( - serverUrl.replace(path: '/download'), - (header) => savePath, - ); - - final f = File(savePath); - expect(f.readAsStringSync(), equals('I am a text file')); - }); - - test('download error', () async { - final savePath = p.join(tmp.path, 'download_error.md'); - final dio = Dio()..options.baseUrl = serverUrl.toString(); - Response response = await dio - .download('/error', savePath) - .catchError((e) => (e as DioException).response!); - expect(response.data, 'error'); - response = await dio - .download( - '/error', - savePath, - options: Options(receiveDataWhenStatusError: false), - ) - .catchError((e) => (e as DioException).response!); - expect(response.data, null); - }); - - test( - 'download timeout', - () async { - final dio = Dio(); - final timeoutMatcher = allOf([ - throwsA(isA()), - throwsA( - predicate( - (e) => e.type == DioExceptionType.receiveTimeout, - ), - ), - ]); - await expectLater( - dio.downloadUri( - Uri.parse('$serverUrl/download').replace( - queryParameters: {'count': '3', 'gap': '2'}, - ), - p.join(tmp.path, 'download_timeout.md'), - options: Options(receiveTimeout: Duration(seconds: 1)), - ), - timeoutMatcher, - ); - // Throws nothing if it constantly gets response bytes. - await dio.download( - 'https://github.com/cfug/flutter.cn/archive/refs/heads/main.zip', - p.join(tmp.path, 'main.zip'), - options: Options(receiveTimeout: Duration(seconds: 1)), - ); - }, - // The download of the main.zip file can be slow, - // so we need to increase the timeout. - timeout: Timeout(Duration(minutes: 1)), - ); - - test('download cancellation', () async { - final savePath = p.join(tmp.path, 'download_cancellation.md'); - final cancelToken = CancelToken(); - Future.delayed(Duration(milliseconds: 100), () { - cancelToken.cancel(); - }); - expect( - Dio() - .download( - '$serverUrl/download', - savePath, - cancelToken: cancelToken, - ) - .catchError((e) => throw (e as DioException).type), - throwsA(DioExceptionType.cancel), - ); - }); - - test('delete on error', () async { - final savePath = p.join(tmp.path, 'delete_on_error.md'); - final f = File(savePath)..createSync(recursive: true); - expect(f.existsSync(), isTrue); - - final dio = Dio()..options.baseUrl = serverUrl.toString(); - await expectLater( - dio - .download( - '/download', - savePath, - deleteOnError: true, - onReceiveProgress: (count, total) => throw AssertionError(), - ) - .catchError((e) => throw (e as DioException).error!), - throwsA(isA()), - ); - expect(f.existsSync(), isFalse); - }); - - test('delete on cancel', () async { - final savePath = p.join(tmp.path, 'delete_on_cancel.md'); - final f = File(savePath)..createSync(recursive: true); - expect(f.existsSync(), isTrue); - - final cancelToken = CancelToken(); - final dio = Dio()..options.baseUrl = serverUrl.toString(); - await expectLater( - dio.download( - '/download', - savePath, - deleteOnError: true, - cancelToken: cancelToken, - onReceiveProgress: (count, total) => cancelToken.cancel(), - ), - throwsDioException( - DioExceptionType.cancel, - stackTraceContains: 'test/download_test.dart', - ), - ); - await Future.delayed(const Duration(milliseconds: 100)); - expect(f.existsSync(), isFalse); - }); - - test('cancel download mid stream', () async { - const savePath = 'test/download/_test.md'; - final f = File(savePath)..createSync(recursive: true); - expect(f.existsSync(), isTrue); - - final cancelToken = CancelToken(); - final dio = Dio()..options.baseUrl = httpbunBaseUrl; - - await expectLater( - dio.download( - '/bytes/10000', - savePath, - cancelToken: cancelToken, - deleteOnError: true, - onReceiveProgress: (c, t) { - if (c > 5000) { - cancelToken.cancel(); - } - }, - ), - throwsDioException( - DioExceptionType.cancel, - stackTraceContains: 'test/download_test.dart', - ), - ); - - await Future.delayed(const Duration(milliseconds: 100)); - expect(f.existsSync(), isFalse); - }); - - test('`savePath` types', () async { - final testPath = p.join(tmp.path, 'savePath'); - - final dio = Dio() - ..options.baseUrl = EchoAdapter.mockBase - ..httpClientAdapter = EchoAdapter(); - - await expectLater( - dio.download('/test', testPath), - completes, - ); - await expectLater( - dio.download('/test', (headers) => testPath), - completes, - ); - await expectLater( - dio.download('/test', (headers) async => testPath), - completes, - ); - }); -} diff --git a/dio/test/options_test.dart b/dio/test/options_test.dart index 64a9611af..af0514a44 100644 --- a/dio/test/options_test.dart +++ b/dio/test/options_test.dart @@ -10,7 +10,6 @@ import 'package:test/test.dart'; import 'mock/adapters.dart'; import 'mock/http_mock.mocks.dart'; -import 'utils.dart'; void main() { test('options', () { diff --git a/dio_test/lib/src/test/download_stream_tests.dart b/dio_test/lib/src/test/download_stream_tests.dart deleted file mode 100644 index 32016b6e4..000000000 --- a/dio_test/lib/src/test/download_stream_tests.dart +++ /dev/null @@ -1,154 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:dio_test/util.dart'; -import 'package:path/path.dart' as p; -import 'package:test/test.dart'; - -void downloadStreamTests( - Dio Function(String baseUrl) create, -) { - group( - 'download', - () { - late Dio dio; - late Directory tmp; - - setUp(() { - dio = create(httpbunBaseUrl); - }); - - setUpAll(() { - tmp = Directory.systemTemp.createTempSync('dio_test_'); - addTearDown(() { - tmp.deleteSync(recursive: true); - }); - }); - - test('bytes', () async { - final path = p.join(tmp.path, 'bytes.txt'); - - final size = 50000; - int progressEventCount = 0; - int count = 0; - int total = 0; - await dio.download( - '/bytes/$size', - path, - onReceiveProgress: (c, t) { - count = c; - total = t; - progressEventCount++; - }, - ); - - final f = File(path); - expect(count, f.readAsBytesSync().length); - expect(progressEventCount, greaterThanOrEqualTo(1)); - expect(count, total); - }); - - test('cancels request', () async { - final cancelToken = CancelToken(); - - final res = await dio.get( - '/drip', - queryParameters: {'duration': '5', 'delay': '0'}, - options: Options(responseType: ResponseType.stream), - cancelToken: cancelToken, - ); - - Future.delayed(const Duration(seconds: 2), () { - cancelToken.cancel(); - }); - - final completer = Completer(); - res.data!.stream.listen((event) {}, onError: (e, s) { - completer.completeError(e, s); - }); - - await expectLater( - completer.future, - throwsDioException( - DioExceptionType.cancel, - stackTraceContains: 'test/download_stream_tests.dart', - ), - ); - }); - - test('cancels download', () async { - final cancelToken = CancelToken(); - final path = p.join(tmp.path, 'download.txt'); - - Future.delayed(const Duration(milliseconds: 50), () { - cancelToken.cancel(); - }); - - await expectLater( - dio.download( - '/drip', - path, - queryParameters: {'duration': '5', 'delay': '0'}, - cancelToken: cancelToken, - ), - throwsDioException( - DioExceptionType.cancel, - stackTraceContains: 'test/download_stream_tests.dart', - ), - ); - - await Future.delayed(const Duration(milliseconds: 250), () {}); - expect(File(path).existsSync(), false); - }); - - test('cancels streamed response mid request', () async { - final cancelToken = CancelToken(); - final response = await dio.get( - '/bytes/${1024 * 1024 * 100}', - options: Options(responseType: ResponseType.stream), - cancelToken: cancelToken, - onReceiveProgress: (c, t) { - if (c > 5000) { - cancelToken.cancel(); - } - }, - ); - - await expectLater( - (response.data as ResponseBody).stream.last, - throwsDioException( - DioExceptionType.cancel, - stackTraceContains: 'test/download_stream_tests.dart', - ), - ); - }); - - test('cancels download mid request', () async { - final cancelToken = CancelToken(); - final path = p.join(tmp.path, 'download_2.txt'); - - await expectLater( - dio.download( - '/bytes/${1024 * 1024 * 10}', - path, - cancelToken: cancelToken, - onReceiveProgress: (c, t) { - if (c > 5000) { - cancelToken.cancel(); - } - }, - ), - throwsDioException( - DioExceptionType.cancel, - stackTraceContains: 'test/download_stream_tests.dart', - ), - ); - - await Future.delayed(const Duration(milliseconds: 250), () {}); - expect(File(path).existsSync(), false); - }); - }, - testOn: 'vm', - ); -} diff --git a/dio_test/lib/src/test/download_tests.dart b/dio_test/lib/src/test/download_tests.dart new file mode 100644 index 000000000..019f52d21 --- /dev/null +++ b/dio_test/lib/src/test/download_tests.dart @@ -0,0 +1,388 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:dio_test/util.dart'; +import 'package:path/path.dart' as p; +import 'package:test/test.dart'; + +void downloadTests( + Dio Function(String baseUrl) create, +) { + group( + 'download', + () { + late Dio dio; + late Directory tmp; + + setUp(() { + dio = create(httpbunBaseUrl); + }); + + setUpAll(() { + tmp = Directory.systemTemp.createTempSync('dio_test_'); + addTearDown(() { + tmp.deleteSync(recursive: true); + }); + }); + + test('bytes', () async { + final path = p.join(tmp.path, 'bytes.txt'); + + final size = 50000; + int progressEventCount = 0; + int count = 0; + int total = 0; + await dio.download( + '/bytes/$size', + path, + onReceiveProgress: (c, t) { + count = c; + total = t; + progressEventCount++; + }, + ); + + final f = File(path); + expect(count, f.readAsBytesSync().length); + expect(progressEventCount, greaterThanOrEqualTo(1)); + expect(count, total); + }); + + test('cancels request', () async { + final cancelToken = CancelToken(); + + final res = await dio.get( + '/drip', + queryParameters: {'duration': '5', 'delay': '0'}, + options: Options(responseType: ResponseType.stream), + cancelToken: cancelToken, + ); + + Future.delayed(const Duration(seconds: 2), () { + cancelToken.cancel(); + }); + + final completer = Completer(); + res.data!.stream.listen((event) {}, onError: (e, s) { + completer.completeError(e, s); + }); + + await expectLater( + completer.future, + throwsDioException( + DioExceptionType.cancel, + stackTraceContains: 'test/download_tests.dart', + ), + ); + }); + + test('cancels download', () async { + final cancelToken = CancelToken(); + final path = p.join(tmp.path, 'download.txt'); + + Future.delayed(const Duration(milliseconds: 50), () { + cancelToken.cancel(); + }); + + await expectLater( + dio.download( + '/drip', + path, + queryParameters: {'duration': '5', 'delay': '0'}, + cancelToken: cancelToken, + ), + throwsDioException( + DioExceptionType.cancel, + stackTraceContains: 'test/download_tests.dart', + ), + ); + + await Future.delayed(const Duration(milliseconds: 250), () {}); + expect(File(path).existsSync(), false); + }); + + test('cancels streamed response mid request', () async { + final cancelToken = CancelToken(); + final response = await dio.get( + '/bytes/${1024 * 1024 * 100}', + options: Options(responseType: ResponseType.stream), + cancelToken: cancelToken, + onReceiveProgress: (c, t) { + if (c > 5000) { + cancelToken.cancel(); + } + }, + ); + + await expectLater( + (response.data as ResponseBody).stream.last, + throwsDioException( + DioExceptionType.cancel, + stackTraceContains: 'test/download_tests.dart', + ), + ); + }); + + test('cancels download mid request', () async { + final cancelToken = CancelToken(); + final path = p.join(tmp.path, 'download_2.txt'); + + await expectLater( + dio.download( + '/bytes/${1024 * 1024 * 10}', + path, + cancelToken: cancelToken, + onReceiveProgress: (c, t) { + if (c > 5000) { + cancelToken.cancel(); + } + }, + ), + throwsDioException( + DioExceptionType.cancel, + stackTraceContains: 'test/download_tests.dart', + ), + ); + + await Future.delayed(const Duration(milliseconds: 250), () {}); + expect(File(path).existsSync(), false); + }); + + test('does not change the response type', () async { + final savePath = p.join(tmp.path, 'download0.md'); + + final options = Options(responseType: ResponseType.plain); + await dio.download('/bytes/1000', savePath, options: options); + expect(options.responseType, ResponseType.plain); + }); + + test('text file', () async { + final savePath = p.join(tmp.path, 'download.txt'); + + int? total; + int? count; + await dio.download( + '/payload', + savePath, + data: 'I am a text file', + options: Options( + contentType: Headers.textPlainContentType, + ), + onReceiveProgress: (c, t) { + total = t; + count = c; + }, + ); + + final f = File(savePath); + expect( + f.readAsStringSync(), + equals('I am a text file'), + ); + expect(count, f.readAsBytesSync().length); + expect(count, total); + }); + + test('text file 2', () async { + final savePath = p.join(tmp.path, 'download2.txt'); + + await dio.downloadUri( + Uri.parse(dio.options.baseUrl).replace(path: '/payload'), + (header) => savePath, + data: 'I am a text file', + options: Options( + contentType: Headers.textPlainContentType, + ), + ); + + final f = File(savePath); + expect( + f.readAsStringSync(), + equals('I am a text file'), + ); + }); + + test('error', () async { + final savePath = p.join(tmp.path, 'download_error.md'); + + expectLater( + dio.download( + '/mix/s=400/b64=${base64Encode('error'.codeUnits)}', + savePath, + ), + throwsDioException( + DioExceptionType.badResponse, + stackTraceContains: 'test/download_tests.dart', + matcher: isA().having( + (e) => e.response!.data, + 'data', + 'error', + ), + ), + ); + + expectLater( + dio.download( + '/mix/s=400/b64=${base64Encode('error'.codeUnits)}', + savePath, + options: Options(receiveDataWhenStatusError: false), + ), + throwsDioException( + DioExceptionType.badResponse, + stackTraceContains: 'test/download_tests.dart', + matcher: isA().having( + (e) => e.response!.data, + 'data', + isNull, + ), + ), + ); + }); + + test( + 'timeout', + () async { + final timeoutMatcher = allOf([ + throwsA(isA()), + throwsA( + predicate( + (e) => e.type == DioExceptionType.receiveTimeout, + ), + ), + ]); + await expectLater( + dio.downloadUri( + Uri.parse('/drip?delay=0&duration=6&numbytes=6').replace( + queryParameters: {'count': '3', 'gap': '2'}, + ), + p.join(tmp.path, 'download_timeout.md'), + options: Options(receiveTimeout: Duration(seconds: 1)), + ), + timeoutMatcher, + ); + + // Throws nothing if it constantly gets response bytes. + await dio.download( + 'https://github.com/cfug/flutter.cn/archive/refs/heads/main.zip', + p.join(tmp.path, 'main.zip'), + options: Options(receiveTimeout: Duration(seconds: 1)), + ); + }, + // The download of the main.zip file can be slow, + // so we need to increase the timeout. + timeout: Timeout(Duration(minutes: 1)), + ); + + test('delete on error', () async { + final savePath = p.join(tmp.path, 'delete_on_error.txt'); + final f = File(savePath)..createSync(recursive: true); + expect(f.existsSync(), isTrue); + + await expectLater( + dio.download( + '/drip?delay=0&duration=5', + savePath, + deleteOnError: true, + onReceiveProgress: (count, total) => throw AssertionError(), + ), + throwsDioException( + DioExceptionType.unknown, + stackTraceContains: 'test/download_tests.dart', + matcher: isA().having( + (e) => e.error, + 'error', + isA(), + ), + ), + ); + + await Future.delayed(const Duration(milliseconds: 100)); + expect(f.existsSync(), isFalse); + }); + + test('delete on cancel', () async { + final savePath = p.join(tmp.path, 'delete_on_cancel.md'); + final f = File(savePath)..createSync(recursive: true); + expect(f.existsSync(), isTrue); + + final cancelToken = CancelToken(); + + await expectLater( + dio.download( + '/bytes/5000', + savePath, + deleteOnError: true, + cancelToken: cancelToken, + onReceiveProgress: (count, total) => cancelToken.cancel(), + ), + throwsDioException( + DioExceptionType.cancel, + stackTraceContains: 'test/download_tests.dart', + ), + ); + + await Future.delayed(const Duration(milliseconds: 100)); + expect(f.existsSync(), isFalse); + }); + + test('cancel download mid stream', () async { + const savePath = 'test/download/_test.md'; + final f = File(savePath)..createSync(recursive: true); + expect(f.existsSync(), isTrue); + + final cancelToken = CancelToken(); + final dio = Dio()..options.baseUrl = httpbunBaseUrl; + + await expectLater( + dio.download( + '/bytes/10000', + savePath, + cancelToken: cancelToken, + deleteOnError: true, + onReceiveProgress: (c, t) { + if (c > 5000) { + cancelToken.cancel(); + } + }, + ), + throwsDioException( + DioExceptionType.cancel, + stackTraceContains: 'test/download_tests.dart', + ), + ); + + await Future.delayed(const Duration(milliseconds: 100)); + expect(f.existsSync(), isFalse); + }); + + test('`savePath` types', () async { + final testPath = p.join(tmp.path, 'savePath.txt'); + + await expectLater( + dio.download( + '/bytes/5000', + testPath, + ), + completes, + ); + await expectLater( + dio.download( + '/bytes/5000', + (headers) => testPath, + ), + completes, + ); + await expectLater( + dio.download( + '/bytes/5000', + (headers) async => testPath, + ), + completes, + ); + }); + }, + testOn: 'vm', + ); +} diff --git a/dio_test/lib/src/test/suite.dart b/dio_test/lib/src/test/suite.dart index e08435b9a..c17211eda 100644 --- a/dio_test/lib/src/test/suite.dart +++ b/dio_test/lib/src/test/suite.dart @@ -9,7 +9,7 @@ const _tests = [ basicTests, cancellationTests, corsTests, - downloadStreamTests, + downloadTests, headerTests, httpMethodTests, parameterTests, diff --git a/dio_test/lib/tests.dart b/dio_test/lib/tests.dart index 0faacec68..2825751e3 100644 --- a/dio_test/lib/tests.dart +++ b/dio_test/lib/tests.dart @@ -1,7 +1,7 @@ export 'src/test/basic_tests.dart'; export 'src/test/cancellation_tests.dart'; export 'src/test/cors_tests.dart'; -export 'src/test/download_stream_tests.dart'; +export 'src/test/download_tests.dart'; export 'src/test/headers_tests.dart'; export 'src/test/http_method_tests.dart'; export 'src/test/parameter_tests.dart';