Browse Source

add test cases

wendux 5 years ago
parent
commit
2929f7a18a

+ 2 - 1
.gitignore

@@ -17,6 +17,7 @@ dio/.dart_tool/
 dio/.pub/
 dio/.idea/
 dio/.exampl
+dio/coverage
 
 # plugins
 plugins/cookie_manager/.packages
@@ -29,4 +30,4 @@ plugins/http2_adapter/.packages
 plugins/http2_adapter/.dart_tool/
 plugins/http2_adapter/.pub/
 plugins/http2_adapter/.idea/
-plugins/http2_adapter/.exampl
+plugins/http2_adapter/.exampl

+ 1 - 1
dio/lib/src/adapters/io_adapter.dart

@@ -86,7 +86,7 @@ class DefaultHttpClientAdapter implements HttpClientAdapter {
       stream,
       responseStream.statusCode,
       headers: headers,
-      isRedirect: responseStream.isRedirect,
+      isRedirect: responseStream.isRedirect||responseStream.redirects.isNotEmpty,
       redirects: responseStream.redirects
           .map((e) => RedirectRecord(e.statusCode, e.method, e.location))
           .toList(),

+ 4 - 4
dio/lib/src/form_data.dart

@@ -10,11 +10,11 @@ class FormData {
   static const String _BOUNDARY_PRE_TAG = '--dio-boundary-';
   static const _BOUNDARY_LENGTH = _BOUNDARY_PRE_TAG.length + 10;
 
-  /// The boundary of FormData, it consists of a constant prefix and a random
-  /// postfix to assure the the boundary unpredictable and unique, each FormData
-  /// instance will be different. And you can custom it by yourself.
   String _boundary;
 
+  /// The boundary of FormData, it consists of a constant prefix and a random
+  /// postfix to assure the the boundary unpredictable and unique, each FormData
+  /// instance will be different.
   String get boundary => _boundary;
 
   final _newlineRegExp = RegExp(r'\r\n|\r|\n');
@@ -154,6 +154,6 @@ class FormData {
 
   ///Transform the entire FormData contents as a list of bytes asynchronously.
   Future<List<int>> readAsBytes() {
-    return finalize().reduce((a, b) => [...a, ...b]);
+    return Future(()=>finalize().reduce((a, b) => [...a, ...b]));
   }
 }

+ 0 - 1
dio/lib/src/headers.dart

@@ -49,7 +49,6 @@ class Headers {
     var arr = this[name];
     if (arr == null) return set(name, value);
     arr.add(value);
-    set(name, arr.join(','));
   }
 
   /// Sets a header. The header named [name] will have all its values

+ 4 - 4
dio/lib/src/options.dart

@@ -220,8 +220,8 @@ class RequestOptions extends Options {
     ResponseType responseType,
     String contentType,
     ValidateStatus validateStatus,
-    bool receiveDataWhenStatusError = true,
-    bool followRedirects = true,
+    bool receiveDataWhenStatusError,
+    bool followRedirects,
     int maxRedirects,
     RequestEncoder requestEncoder,
     ResponseDecoder responseDecoder,
@@ -260,8 +260,8 @@ class RequestOptions extends Options {
     ResponseType responseType,
     String contentType,
     ValidateStatus validateStatus,
-    bool receiveDataWhenStatusError = true,
-    bool followRedirects = true,
+    bool receiveDataWhenStatusError,
+    bool followRedirects,
     int maxRedirects,
     RequestEncoder requestEncoder,
     ResponseDecoder responseDecoder,

+ 5 - 2
dio/pubspec.yaml

@@ -1,6 +1,6 @@
 name: dio
 description: A powerful Http client for Dart, which supports Interceptors, FormData, Request Cancellation, File Downloading, Timeout etc.
-version: 3.0.9
+version: 3.0.10
 homepage: https://github.com/flutterchina/dio
 
 environment:
@@ -12,5 +12,8 @@ dependencies:
 
 dev_dependencies:
   test: ^1.5.1
-  pedantic: '1.1.0'
+  pedantic: ^1.8.0
+  test_coverage: ^0.4.1
+  flutter_test:
+    sdk: flutter
 

+ 115 - 2
dio/test/basic_test.dart

@@ -2,22 +2,136 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 @TestOn('vm')
-
 import 'dart:async';
 import 'dart:io';
 import 'package:dio/dio.dart';
 import 'package:test/test.dart';
 
 void main() {
+  test('#test options', () {
+    var map = {'a': '5'};
+    var mapOverride = {'b': '6'};
+    var baseOptions = BaseOptions(
+      connectTimeout: 2000,
+      receiveTimeout: 2000,
+      sendTimeout: 2000,
+      baseUrl: 'http://localhost',
+      queryParameters: map,
+      extra: map,
+      headers: map,
+      contentType: "application/json",
+      followRedirects: false,
+    );
+    var opt1 = baseOptions.merge(
+      method: 'post',
+      receiveTimeout: 3000,
+      sendTimeout: 3000,
+      baseUrl: 'https://flutterchina.club',
+      extra: mapOverride,
+      headers: mapOverride,
+      contentType: 'text/html',
+    );
+    assert(opt1.method == "post");
+    assert(opt1.receiveTimeout == 3000);
+    assert(opt1.connectTimeout == 2000);
+    assert(opt1.followRedirects == false);
+    assert(opt1.baseUrl == 'https://flutterchina.club');
+    assert(opt1.headers['b'] == '6');
+    assert(opt1.extra['b'] == '6');
+    assert(opt1.queryParameters['b'] == null);
+    assert(opt1.contentType == 'text/html');
+
+    var opt2 = Options(
+      method: 'get',
+      receiveTimeout: 2000,
+      sendTimeout: 2000,
+      extra: map,
+      headers: map,
+      contentType: "application/json",
+      followRedirects: false,
+    );
+    var opt3 = opt2.merge(
+      method: 'post',
+      receiveTimeout: 3000,
+      sendTimeout: 3000,
+      extra: mapOverride,
+      headers: mapOverride,
+      contentType: 'text/html',
+    );
+    assert(opt3.method == "post");
+    assert(opt3.receiveTimeout == 3000);
+    assert(opt3.followRedirects == false);
+    assert(opt3.headers['b'] == '6');
+    assert(opt3.extra['b'] == '6');
+    assert(opt3.contentType == 'text/html');
+
+    var opt4 = RequestOptions(
+      sendTimeout: 2000,
+      followRedirects: false,
+    );
+    var opt5 = opt4.merge(
+      method: 'post',
+      receiveTimeout: 3000,
+      sendTimeout: 3000,
+      extra: mapOverride,
+      headers: mapOverride,
+      data: "xx=5",
+      path: '/',
+      contentType: 'text/html',
+    );
+    assert(opt5.method == "post");
+    assert(opt5.receiveTimeout == 3000);
+    assert(opt5.followRedirects == false);
+    assert(opt5.contentType == 'text/html');
+    assert(opt5.headers['b'] == '6');
+    assert(opt5.extra['b'] == '6');
+    assert(opt5.data == 'xx=5');
+    assert(opt5.path == '/');
+  });
+
+  test('#test headers', () {
+    var headers = Headers.fromMap({
+      "set-cookie": ['k=v', 'k1=v1'],
+      'content-length': ['200'],
+      'test': ['1', '2'],
+    });
+    headers.add('SET-COOKIE', 'k2=v2');
+    assert(headers.value('content-length') == '200');
+    expect(Future(() => headers.value('test')), throwsException);
+    assert(headers['set-cookie'].length == 3);
+    headers.remove("set-cookie", 'k=v');
+    assert(headers['set-cookie'].length == 2);
+    headers.removeAll('set-cookie');
+    assert(headers['set-cookie'] == null);
+    var ls = [];
+    headers.forEach((k, list) {
+      ls.addAll(list);
+    });
+    assert(ls.length == 3);
+    assert(headers.toString() == "content-length: 200\ntest: 1\ntest: 2\n");
+    headers.set('content-length', '300');
+    assert(headers.value('content-length') == '300');
+    headers.set('content-length', ['400']);
+    assert(headers.value('content-length') == '400');
+
+    var headers1 = Headers();
+    headers1.set('xx', 'v');
+    assert(headers1.value('xx') == 'v');
+    headers1.clear();
+    assert(headers1.map.isEmpty == true);
+  });
+
   test('#send with an invalid URL', () {
     expect(Dio().get('http://http.invalid').catchError((e) => throw e.error),
         throwsA(const TypeMatcher<SocketException>()));
   });
+
   test('#cancellation', () async {
     var dio = Dio();
     CancelToken token = CancelToken();
     Timer(Duration(milliseconds: 10), () {
       token.cancel('cancelled');
+      dio.httpClientAdapter.close(force: true);
     });
 
     var url = 'https://accounts.google.com';
@@ -28,7 +142,6 @@ void main() {
         throwsA(isTrue));
   });
 
-
   test('#url encode ', () {
     var data = {
       'a': '你好',

+ 69 - 8
dio/test/download_test.dart

@@ -12,19 +12,80 @@ import 'utils.dart';
 void main() {
   setUp(startServer);
   tearDown(stopServer);
-  test('#test download', () async {
+  test('#test download1', () async {
     const savePath = '../_download_test.md';
     var dio = Dio();
     dio.options.baseUrl = serverUrl.toString();
-    await dio.download('/download', savePath, // disable gzip
-        onReceiveProgress: (received, total) {
-      if (total != -1) {
-        print((received / total * 100).toStringAsFixed(0) + '%');
-      }
-    });
+    await dio.download(
+      '/download', savePath, // disable gzip
+      onReceiveProgress: (received, total) {
+        // ignore progress
+      },
+    );
 
     var f = File(savePath);
     expect(f.readAsStringSync(), equals('I am a text file'));
-    f.delete(recursive: false);
+    f.deleteSync(recursive: false);
+  });
+
+  test('#test download2', () async {
+    const savePath = '../_download_test.md';
+    var dio = Dio();
+    dio.options.baseUrl = serverUrl.toString();
+    await dio.downloadUri(
+      serverUrl.replace(path: '/download'),
+      (header) => savePath, // disable gzip
+    );
+
+    var f = File(savePath);
+    expect(f.readAsStringSync(), equals('I am a text file'));
+    f.deleteSync(recursive: false);
+  });
+
+  test('#test download error', () async {
+    const savePath = '../_download_test.md';
+    var dio = Dio();
+    dio.options.baseUrl = serverUrl.toString();
+    Response r =
+        await dio.download('/error', savePath).catchError((e) => e.response);
+    assert(r.data == 'error');
+    r = await dio
+        .download(
+          '/error',
+          savePath,
+          options: Options(receiveDataWhenStatusError: false),
+        )
+        .catchError((e) => e.response);
+    assert(r.data == null);
+  });
+
+  test('#test download timeout', () async {
+    const savePath = '../_download_test.md';
+    var dio = Dio(BaseOptions(
+      receiveTimeout: 100,
+      baseUrl: serverUrl.toString(),
+    ));
+    expect(dio.download('/download', savePath).catchError((e) => throw e.type),
+        throwsA(DioErrorType.RECEIVE_TIMEOUT));
+    //print(r);
+  });
+
+  test('#test download cancellation', () async {
+    const savePath = '../_download_test.md';
+    var cancelToken = CancelToken();
+    Future.delayed(Duration(milliseconds: 100), () {
+      cancelToken.cancel();
+    });
+    expect(
+      Dio()
+          .download(
+            serverUrl.toString() + '/download',
+            savePath,
+            cancelToken: cancelToken,
+          )
+          .catchError((e) => throw e.type),
+      throwsA(DioErrorType.CANCEL),
+    );
+    //print(r);
   });
 }

+ 28 - 16
dio/test/formdata_test.dart

@@ -2,7 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 @TestOn('vm')
-
 import 'dart:convert';
 import 'dart:io';
 import 'package:dio/dio.dart';
@@ -10,22 +9,35 @@ import 'package:test/test.dart';
 
 void main() {
   test('#test FormData', () async {
-    var fm=FormData.fromMap({
-      "name": "wendux",
-      "age": 25,
-      "file": MultipartFile.fromString("hellow world."),
-      "files": [
-        await MultipartFile.fromFile("../dio/test/_testfile",
-            filename: "1.txt"),
-        MultipartFile.fromFileSync("../dio/test/_testfile",
-            filename: "2.txt"),
+    var fm = FormData.fromMap({
+      'name': 'wendux',
+      'age': 25,
+      'file': MultipartFile.fromString('hellow world.'),
+      'files': [
+        await MultipartFile.fromFile('../dio/test/_testfile',
+            filename: '1.txt'),
+        MultipartFile.fromFileSync('../dio/test/_testfile', filename: '2.txt'),
       ]
     });
-    var fmStr=await fm.readAsBytes();
-    var f=File("../dio/test/_formdata") ;
-    var content=f.readAsStringSync();
-    content=content.replaceAll("--dio-boundary-3788753558",fm.boundary);
-    assert(utf8.decode(fmStr,allowMalformed: true)==content);
-  });
+    var fmStr = await fm.readAsBytes();
+    var f = File('../dio/test/_formdata');
+    var content = f.readAsStringSync();
+    content = content.replaceAll('--dio-boundary-3788753558', fm.boundary);
+    assert(utf8.decode(fmStr, allowMalformed: true) == content);
+    expect(fm.readAsBytes(),throwsA(const TypeMatcher<StateError>()));
 
+    var fm1 = FormData();
+    fm1.fields.add(MapEntry('name', 'wendux'));
+    fm1.fields.add(MapEntry('age', '25'));
+    fm1.files.add(MapEntry('file', MultipartFile.fromString('hellow world.')));
+    fm1.files.add(MapEntry(
+      'files[]',
+      await MultipartFile.fromFile('../dio/test/_testfile', filename: '1.txt'),
+    ));
+    fm1.files.add(MapEntry(
+      'files[]',
+      await MultipartFile.fromFile('../dio/test/_testfile', filename: '2.txt'),
+    ));
+    assert(fmStr.length==fm1.length);
+  });
 }

+ 16 - 0
dio/test/interceptor_test.dart

@@ -4,6 +4,16 @@ import 'package:dio/dio.dart';
 import 'package:test/test.dart';
 import 'mock_adapter.dart';
 
+class MyInterceptor extends Interceptor {
+  int requestCount = 0;
+
+  @override
+  Future onRequest(RequestOptions options) {
+    requestCount++;
+    return super.onRequest(options);
+  }
+}
+
 void main() {
   group('#test Request Interceptor', () {
     Dio dio;
@@ -108,6 +118,8 @@ void main() {
       Dio tokenDio = Dio();
       dio.options.baseUrl = tokenDio.options.baseUrl = MockAdapter.mockBase;
       dio.httpClientAdapter = tokenDio.httpClientAdapter = MockAdapter();
+      var myInter = MyInterceptor();
+      dio.interceptors.add(myInter);
       dio.interceptors
           .add(InterceptorsWrapper(onRequest: (RequestOptions options) {
         if (csrfToken == null) {
@@ -135,6 +147,10 @@ void main() {
       ]);
       expect(tokenRequestCounts, 1);
       expect(result, 3);
+      assert(myInter.requestCount > 0);
+      dio.interceptors[0] = myInter;
+      dio.interceptors.clear();
+      assert(dio.interceptors.isEmpty == true);
     });
   });
 }

+ 3 - 3
dio/test/mock_adapter.dart

@@ -44,11 +44,11 @@ class MockAdapter extends HttpClientAdapter {
         case "/token":
           {
             var t = "ABCDEFGHIJKLMN".split("")..shuffle();
-            return ResponseBody.fromString(
-              jsonEncode({
+            return ResponseBody.fromBytes(
+              utf8.encode(jsonEncode({
                 "errCode": 0,
                 "data": {"token": t.join()}
-              }),
+              })),
               200,
               headers: {
                 Headers.contentTypeHeader: [Headers.jsonContentType],

+ 67 - 0
dio/test/request_test.dart

@@ -20,7 +20,16 @@ void main() {
       dio = Dio();
       dio.options
         ..baseUrl = serverUrl.toString()
+        ..connectTimeout = 1000
+        ..receiveTimeout = 5000
         ..headers = {'User-Agent': 'dartisan'};
+      dio.interceptors.add(LogInterceptor(
+        responseBody: true,
+        requestBody: true,
+        logPrint: (log) => {
+          // ignore log
+        },
+      ));
     });
     test('#test restful APIs', () async {
       Response response;
@@ -61,6 +70,64 @@ void main() {
       expect(dio.get('/error').catchError((e) => throw e.response.statusCode),
           throwsA(equals(400)));
 
+      // redirect test
+      response = await dio.get(
+        '/redirect',
+        onReceiveProgress: (received, total) {
+          // ignore progress
+        },
+      );
+      assert(response.isRedirect == true);
+      assert(response.redirects.length == 1);
+      var ri = response.redirects.first;
+      assert(ri.statusCode == 302);
+      assert(ri.method == "GET");
+    });
+
+    test('#test request with URI', () async {
+      Response response;
+
+      // test get
+      response = await dio.getUri(
+        Uri(path: '/test', queryParameters: {'id': '12', 'name': 'wendu'}),
+      );
+      expect(response.statusCode, 200);
+      expect(response.isRedirect, false);
+      expect(response.data['query'], equals('id=12&name=wendu'));
+      expect(response.headers.value('single'), equals('value'));
+
+      const map = {'content': 'I am playload'};
+
+      // test post
+      response = await dio.postUri(Uri(path: '/test'), data: map);
+      expect(response.data['method'], 'POST');
+      expect(response.data['body'], jsonEncode(map));
+
+      // test put
+      response = await dio.putUri(Uri(path: '/test'), data: map);
+      expect(response.data['method'], 'PUT');
+      expect(response.data['body'], jsonEncode(map));
+
+      // test patch
+      response = await dio.patchUri(Uri(path: '/test'), data: map);
+      expect(response.data['method'], 'PATCH');
+      expect(response.data['body'], jsonEncode(map));
+
+      // test head
+      response = await dio.deleteUri(Uri(path: '/test'), data: map);
+      expect(response.data['method'], 'DELETE');
+      expect(response.data['path'], '/test');
+    });
+
+    test('#test redirect', () async {
+      Response response;
+      response = await dio.get('/redirect');
+      assert(response.isRedirect == true);
+      assert(response.redirects.length == 1);
+      var ri = response.redirects.first;
+      assert(ri.statusCode == 302);
+      assert(ri.method == "GET");
+      assert(ri.location.path == '/');
     });
 
     test('#test generic parameters', () async {

+ 10 - 5
dio/test/utils.dart

@@ -16,7 +16,7 @@ HttpServer _server;
 
 Encoding requiredEncodingForCharset(String charset) =>
     Encoding.getByName(charset) ??
-        (throw FormatException('Unsupported encoding "$charset".'));
+    (throw FormatException('Unsupported encoding "$charset".'));
 
 /// The URL for the current server instance.
 Uri get serverUrl => Uri.parse('http://localhost:${_server.port}');
@@ -29,9 +29,11 @@ Future<void> startServer() async {
       var response = request.response;
 
       if (path == '/error') {
+        const content = 'error';
         response
           ..statusCode = 400
-          ..contentLength = 0;
+          ..contentLength = content.length
+          ..write(content);
         unawaited(response.close());
         return;
       }
@@ -66,8 +68,7 @@ Future<void> startServer() async {
       }
 
       if (path == '/list') {
-        response.headers.contentType =
-            ContentType('application', 'json');
+        response.headers.contentType = ContentType('application', 'json');
         response
           ..statusCode = 200
           ..contentLength = -1
@@ -78,11 +79,15 @@ Future<void> startServer() async {
 
       if (path == "/download") {
         const content = 'I am a text file';
+        response.headers.set("content-encoding", 'plain');
         response
           ..statusCode = 200
           ..contentLength = content.length
           ..write(content);
-        unawaited(response.close());
+
+        Future.delayed(Duration(milliseconds: 300), () {
+          response.close();
+        });
         return;
       }
 

+ 4 - 1
test.sh

@@ -1,3 +1,6 @@
 cd dio
-pub run test
+flutter test --coverage .
+genhtml -o coverage coverage/lcov.info
+# Open in the default browser (mac):
+open coverage/index.html