diff --git a/OAT.xml b/OAT.xml
index c72788ac93f6427d06e8ad04814670b7add779d5..44533568e048e50eb7eec40678501b04bfb0054b 100644
--- a/OAT.xml
+++ b/OAT.xml
@@ -73,6 +73,14 @@
+
+
+
+
+
+
+
+
diff --git a/ohos/async_test/.gitignore b/ohos/async_test/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..24476c5d1eb55824c76d8b01a3965f94abad1ef8
--- /dev/null
+++ b/ohos/async_test/.gitignore
@@ -0,0 +1,44 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/ohos/async_test/README.md b/ohos/async_test/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/ohos/async_test/analysis_options.yaml b/ohos/async_test/analysis_options.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..7814013c895a5ab88ab0bb58d8cfa716f9be5654
--- /dev/null
+++ b/ohos/async_test/analysis_options.yaml
@@ -0,0 +1,42 @@
+# Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at
+ # https://dart-lang.github.io/linter/lints/index.html.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/ohos/async_test/lib/common/base_page.dart b/ohos/async_test/lib/common/base_page.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b177b5b1e64c7231175a3b0b9a6c0891c4190f1f
--- /dev/null
+++ b/ohos/async_test/lib/common/base_page.dart
@@ -0,0 +1,55 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:flutter/material.dart';
+
+import 'main_item_widget.dart';
+import 'test_route.dart';
+
+/// 全局静态数据存储
+abstract class GlobalData {
+ static String appName = '';
+}
+
+/// app基本首页
+class BasePage extends StatefulWidget {
+ const BasePage({required this.data});
+
+ final List data;
+
+ @override
+ State createState() => _BasePageState();
+}
+
+class _BasePageState extends State {
+ int get _itemCount => widget.data.length;
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Center(
+ child: Text(GlobalData.appName, textAlign: TextAlign.center)),
+ ),
+ body:
+ ListView.builder(itemBuilder: _itemBuilder, itemCount: _itemCount));
+ }
+
+ Widget _itemBuilder(BuildContext context, int index) {
+ return MainItemWidget(widget.data[index], (MainItem item) {
+ Navigator.push(context, MaterialPageRoute(builder: (content) => item.route));
+ });
+ }
+}
diff --git a/ohos/async_test/lib/common/item_widget.dart b/ohos/async_test/lib/common/item_widget.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c819693a73244796d1bf5cf7bae12eb07503da2e
--- /dev/null
+++ b/ohos/async_test/lib/common/item_widget.dart
@@ -0,0 +1,128 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+import 'test_page.dart';
+
+/// Item widget.
+class ItemWidget extends StatefulWidget {
+ /// Item widget.
+ const ItemWidget(
+ {required this.item, required this.index, required this.getGroupRange, required this.runGroup, required this.onTap, this.summary, Key? key})
+ : super(key: key);
+
+ /// item summary.
+ final String? summary;
+
+ /// item data.
+ final Item item;
+
+ /// 当前下标
+ final int index;
+
+ /// 获取对应的组信息
+ final GroupRange Function() getGroupRange;
+
+ /// 获取对应的组信息
+ final void Function(int start, int end) runGroup;
+
+ /// Action when pressed (typically run).
+ final void Function(Item item) onTap;
+
+ @override
+ ItemWidgetState createState() => ItemWidgetState();
+}
+
+class ItemWidgetState extends State {
+ @override
+ Widget build(BuildContext context) {
+ IconData? icon;
+ Color? color;
+
+ switch (widget.item.state) {
+ case ItemState.none:
+ icon = Icons.arrow_forward_ios;
+ break;
+ case ItemState.running:
+ icon = Icons.more_horiz;
+ break;
+ case ItemState.success:
+ icon = Icons.check;
+ color = Colors.green;
+ break;
+ case ItemState.failure:
+ icon = Icons.close;
+ color = Colors.red;
+ break;
+ }
+
+ final Widget listTile = ListTile(
+ leading: SizedBox(
+ child: IconButton(
+ icon: Icon(icon, color: color),
+ onPressed: null,
+ )),
+ title: Text(widget.item.name),
+ subtitle: widget.summary != null ? Text(widget.summary!) : null,
+ onTap: () {
+ widget.onTap(widget.item);
+ });
+
+ final data = widget.getGroupRange();
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ if (data.groupName.isNotEmpty && data.startIndex == widget.index)
+ GestureDetector(
+ onTap: () {},
+ child: Container(
+ height: 35,
+ decoration: BoxDecoration(color: CupertinoColors.extraLightBackgroundGray),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Expanded(child: Text(
+ '测试组: ${data.groupName}',
+ style: TextStyle(fontSize: 18),
+ overflow: TextOverflow.ellipsis,
+ )),
+ // FilledButton(
+ // onPressed: () => widget.runGroup(data.startIndex, data.startIndex),
+ // child: Text(
+ // '整组测试',
+ // style: TextStyle(fontSize: 16),
+ // ))
+ ],
+ ),
+ ),
+ ),
+ Container(
+ margin: data.groupName.isNotEmpty && data.startIndex == widget.index ? EdgeInsets.only(bottom: 10) : null,
+ decoration: BoxDecoration(
+ border: data.groupName.isNotEmpty && data.endIndex == widget.index ? Border(bottom: BorderSide(color: Colors.grey)) : null,
+ ),
+ child: Padding(
+ padding: data.groupName.isNotEmpty && data.startIndex <= widget.index && data.endIndex >= widget.index ? EdgeInsets.only(left: 35) : EdgeInsets.zero,
+ child: listTile,
+ ),
+ )
+ ],
+ );
+ }
+}
diff --git a/ohos/async_test/lib/common/main_item_widget.dart b/ohos/async_test/lib/common/main_item_widget.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e74bc192352277bded3cfe99cea44f59652eb61e
--- /dev/null
+++ b/ohos/async_test/lib/common/main_item_widget.dart
@@ -0,0 +1,51 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+import 'test_route.dart';
+
+/// Main item widget.
+class MainItemWidget extends StatefulWidget {
+ /// Main item widget.
+ const MainItemWidget(this.item, this.onTap, {Key? key}) : super(key: key);
+
+ /// item data.
+ final MainItem item;
+
+ /// onTap action (typically run or open).
+ final void Function(MainItem item) onTap;
+
+ @override
+ MainItemWidgetState createState() => MainItemWidgetState();
+}
+
+class MainItemWidgetState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ margin: const EdgeInsets.only(bottom: 10),
+ child: ListTile(
+ tileColor: CupertinoColors.extraLightBackgroundGray,
+ title: Text(widget.item.title),
+ onTap: _onTap),
+ );
+ }
+
+ void _onTap() {
+ widget.onTap(widget.item);
+ }
+}
diff --git a/ohos/async_test/lib/common/test_model_app.dart b/ohos/async_test/lib/common/test_model_app.dart
new file mode 100644
index 0000000000000000000000000000000000000000..9248d4e6da17ff0cfb6f6144b0eaf29648c45fd9
--- /dev/null
+++ b/ohos/async_test/lib/common/test_model_app.dart
@@ -0,0 +1,51 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:flutter/material.dart';
+
+import 'base_page.dart';
+import 'test_route.dart';
+
+/// 基础app框架
+class TestModelApp extends StatefulWidget {
+ TestModelApp({super.key, required this.appName, required this.data}) {
+ GlobalData.appName = appName;
+ }
+
+ /// 测试包名称
+ final String appName;
+
+ /// 路由数据
+ final List data;
+
+ @override
+ State createState() => TestModelState();
+}
+
+class TestModelState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: widget.appName,
+ theme: ThemeData(
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
+ appBarTheme: const AppBarTheme(backgroundColor: Colors.blue),
+ primarySwatch: Colors.blue,
+ useMaterial3: true,
+ ),
+ home: BasePage(data: widget.data),
+ );
+ }
+}
diff --git a/ohos/async_test/lib/common/test_page.dart b/ohos/async_test/lib/common/test_page.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e49098fad9fc80070140a6dfe1565aa54563d1f8
--- /dev/null
+++ b/ohos/async_test/lib/common/test_page.dart
@@ -0,0 +1,334 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+
+import 'item_widget.dart';
+
+List contentList = [];
+
+class Test {
+ /// Test definition.
+ Test(this.name, this.fn, {bool? solo, bool? skip})
+ : solo = solo == true,
+ skip = skip == true;
+
+ /// Only run this test.
+ final bool solo;
+
+ /// Skip this test.
+ final bool skip;
+
+ /// Test name.
+ String name;
+
+ /// Test body.
+ FutureOr Function() fn;
+}
+
+/// Item states.
+enum ItemState {
+ /// test not run yet.
+ none,
+
+ /// test is running.
+ running,
+
+ /// test succeeded.
+ success,
+
+ /// test fails.
+ failure
+}
+
+/// Menu item.
+class Item {
+ /// Menu item.
+ Item(this.name);
+
+ /// Menu item state.
+ ItemState state = ItemState.running;
+
+ /// Menu item name/
+ String name;
+}
+
+class TestLength {
+ TestLength(this.oldLength, this.newLength);
+
+ int oldLength;
+ int newLength;
+}
+
+class GroupRange {
+ GroupRange(this.groupName, this.startIndex, this.endIndex);
+
+ String groupName;
+ int startIndex;
+ int endIndex;
+}
+
+/// 基础测试页面
+class TestPage extends StatefulWidget {
+ /// Base test page.
+ TestPage(this.title, {Key? key}) : super(key: key);
+
+ /// The title.
+ final String title;
+
+ /// Test list.
+ final List tests = [];
+
+ /// 保存group的范围信息
+ final Map groupTitle = {};
+
+ /// define a test.
+ void test(String name, FutureOr Function() fn) {
+ tests.add(Test(name, fn));
+ }
+
+ /// define a group test.
+ void group(String name, FutureOr Function() fn) {
+ int oldLength = tests.length;
+ fn();
+
+ int newLength = tests.length - 1;
+ groupTitle.addAll({name: TestLength(oldLength, newLength)});
+ }
+
+ /// Thrown an exception
+ void fail([String? message]) {
+ throw Exception(message ?? 'should fail');
+ }
+
+ @override
+ TestPageState createState() => TestPageState();
+}
+
+/// Group.
+mixin Group {
+ /// List of tests.
+ List get tests {
+ // TODO: implement tests
+ throw UnimplementedError();
+ }
+
+ bool? _hasSolo;
+ final _tests = [];
+
+ /// Add a test.
+ void add(Test test) {
+ if (!test.skip) {
+ if (test.solo) {
+ if (_hasSolo != true) {
+ _hasSolo = true;
+ _tests.clear();
+ }
+ _tests.add(test);
+ } else if (_hasSolo != true) {
+ _tests.add(test);
+ }
+ }
+ }
+
+ /// true if it has solo or contains item with solo feature
+ bool? get hasSolo => _hasSolo;
+}
+
+class TestPageState extends State with Group {
+ List- items = [];
+
+ Future _run() async {
+ if (!mounted) {
+ return null;
+ }
+
+ setState(() {
+ items.clear();
+ });
+ _tests.clear();
+ for (var test in widget.tests) {
+ add(test);
+ }
+ for (var test in _tests) {
+ var item = Item(test.name);
+ contentList.add(Text(test.name,
+ style: const TextStyle(fontSize: 18, color: Colors.green)));
+
+ late int position;
+ setState(() {
+ position = items.length;
+ items.add(item);
+ });
+ try {
+ await test.fn();
+ item = Item(test.name)..state = ItemState.success;
+ print('ohFlutter: ${test.name}, result: success');
+ } catch (e, st) {
+ contentList.add(Text('$e, $st',
+ style: const TextStyle(fontSize: 18, color: Colors.red)));
+ print('ohFlutter: ${test.name}-error: $e, $st}');
+ item = Item(test.name)..state = ItemState.failure;
+ }
+
+ if (!mounted) {
+ return null;
+ }
+
+ setState(() {
+ items[position] = item;
+ });
+ }
+ }
+
+ Future _runTest(int index) async {
+ if (!mounted) {
+ return null;
+ }
+
+ final test = _tests[index];
+
+ var item = items[index];
+ setState(() {
+ contentList = [];
+ item.state = ItemState.running;
+ });
+ contentList.add(Text(test.name,
+ style: const TextStyle(fontSize: 18, color: Colors.green)));
+ try {
+ await test.fn();
+
+ item = Item(test.name)..state = ItemState.success;
+ print('ohFlutter: ${test.name}, result: success');
+ } catch (e, st) {
+ contentList.add(Text('$e, $st',
+ style: const TextStyle(fontSize: 18, color: Colors.red)));
+ print('ohFlutter: ${test.name}-error: $e, $st}');
+ try {
+ print(st);
+ } catch (_) {}
+ item = Item(test.name)..state = ItemState.failure;
+ }
+
+ if (!mounted) {
+ return null;
+ }
+
+ setState(() {
+ items[index] = item;
+ });
+ showAlertDialog(context);
+ }
+
+ @override
+ void initState() {
+ super.initState();
+ contentList = [];
+ _run();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: Text(widget.title),
+ actions:[
+ IconButton(
+ onPressed: () {
+ showAlertDialog(context);
+ },
+ icon: const Icon(Icons.search_outlined),
+ )
+ ]
+ ),
+ body: ListView(children: [
+ ...items.asMap().keys.map((e) => _itemBuilder(context, e)).toList(),
+ ]));
+ }
+
+ Widget _itemBuilder(BuildContext context, int index) {
+ final item = getItem(index);
+ return ItemWidget(
+ item: item,
+ index: index,
+ getGroupRange: () {
+ GroupRange data = GroupRange('', 0, 0);
+ widget.groupTitle.forEach((key, value) {
+ if (value.oldLength <= index && value.newLength >= index) {
+ data = GroupRange(key, value.oldLength, value.newLength);
+ }
+ });
+ return data;
+ },
+ runGroup: (start, end) async {
+ for (var i = start; i <= end; i++) {
+ await _runTest(i);
+ print('\n');
+ }
+ },
+ onTap: (Item item) {
+ _runTest(index);
+ }
+ );
+ }
+
+ Item getItem(int index) {
+ return items[index];
+ }
+
+ @override
+ List get tests => widget.tests;
+}
+
+void expect(var testModel) {
+ try {
+ testModel;
+ contentList.add(Text('$testModel'));
+ } catch (e) {
+ contentList.add(Text(
+ '$e',
+ style: const TextStyle(color: Colors.red),
+ ));
+ print(e.toString());
+ }
+}
+
+void showAlertDialog(BuildContext context) {
+ for (int i = 0; i < contentList.length; i++) {
+ print(contentList[i].data);
+ }
+ showDialog(
+ context: context,
+ barrierDismissible: false,
+ builder: (BuildContext context) {
+ return AlertDialog(
+ content: SingleChildScrollView(
+ child: Column(
+ children: contentList,
+ ),
+ ),
+ actions: [
+ MaterialButton(
+ child: const Text('确定'),
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ ),
+ ],
+ );
+ });
+}
diff --git a/ohos/async_test/lib/common/test_route.dart b/ohos/async_test/lib/common/test_route.dart
new file mode 100644
index 0000000000000000000000000000000000000000..64478b233caa2d718c7c63b8477c0021406cd59a
--- /dev/null
+++ b/ohos/async_test/lib/common/test_route.dart
@@ -0,0 +1,29 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:flutter/cupertino.dart';
+
+import 'base_page.dart';
+
+class MainItem {
+ /// Main item.
+ MainItem(this.title, this.route);
+
+ /// title.
+ String title;
+
+ /// Page route.
+ Widget route;
+}
diff --git a/ohos/async_test/lib/main.dart b/ohos/async_test/lib/main.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6a2e9f00ec15f28830913551f9fd0b30b95a1e26
--- /dev/null
+++ b/ohos/async_test/lib/main.dart
@@ -0,0 +1,80 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:async_test/src/AsyncCacheTestPage.dart';
+import 'package:async_test/src/AsyncMemoizerTestPage.dart';
+import 'package:async_test/src/ByteCollectionTestPage.dart';
+import 'package:async_test/src/CancelableOperationTestPage.dart';
+import 'package:async_test/src/ChunkedStreamReaderTestPage.dart';
+import 'package:async_test/src/FutureGroupTestPage.dart';
+import 'package:async_test/src/LazyStreamTestPage.dart';
+import 'package:async_test/src/NullStreamSinkTestPage.dart';
+import 'package:async_test/src/RejectErrorsTestPage.dart';
+import 'package:async_test/src/RestartableTimerTestPage.dart';
+import 'package:async_test/src/ResultTestPage.dart';
+import 'package:async_test/src/SingleSubscriptionTransformerTestPage.dart';
+import 'package:async_test/src/SinkBaseTestPage.dart';
+import 'package:async_test/src/StreamCloserTestPage.dart';
+import 'package:async_test/src/StreamCompleterTestPage.dart';
+import 'package:async_test/src/StreamExtensionsTestPage.dart';
+import 'package:async_test/src/StreamGroupTestPage.dart';
+import 'package:async_test/src/StreamQueueTestPage.dart';
+import 'package:async_test/src/StreamSinkCompleterTestPage.dart';
+import 'package:async_test/src/StreamSinkTransformerTestPage.dart';
+import 'package:async_test/src/StreamSplitterTestPage.dart';
+import 'package:async_test/src/StreamSubscriptionTestPage.dart';
+import 'package:async_test/src/StreamZipTestPage.dart';
+import 'package:async_test/src/StreamZipZoneTestPage.dart';
+import 'package:async_test/src/SubscriptionStreamTestPage.dart';
+import 'package:async_test/src/SubscriptionTransformerTestPage.dart';
+import 'package:flutter/material.dart';
+
+import 'common/test_model_app.dart';
+import 'common/test_route.dart';
+
+void main() {
+ final app = [
+ MainItem('result_test', ResultTestPage('result_test')),
+ MainItem('stream_subscription_test', StreamSubscriptionTestPage('stream_subscription_test')),
+ MainItem('async_cache_test', AsyncCacheTestPage('async_cache_test')),
+ MainItem('async_memoizer_test', AsyncMemoizerTestPage('async_memoizer_test')),
+ MainItem('byte_collection_test', ByteCollectionTestPage('byte_collection_test')),
+ MainItem('cancelable_operation_test', CancelableOperationTestPage('cancelable_operation_test')),
+ MainItem('chunked_stream_reader_test', ChunkedStreamReaderTestPage('chunked_stream_reader_test')),
+ MainItem('future_group_test', FutureGroupTestPage('future_group_test')),
+ MainItem('lazy_stream_test', LazyStreamTestPage('lazy_stream_test')),
+ MainItem('null_stream_sink_test', NullStreamSinkTestPage('null_stream_sink_test')),
+ MainItem('reject_errors_test', RejectErrorsTestPage('reject_errors_test')),
+ MainItem('restartable_timer_test', RestartableTimerTestPage('restartable_timer_test')),
+ MainItem('single_subscription_transformer_test', SingleSubscriptionTransformerTestPage('single_subscription_transformer_test')),
+ MainItem('sink_base_test', SinkBaseTestPage('sink_base_test')),
+ MainItem('stream_closer_test', StreamCloserTestPage('stream_closer_test')),
+ MainItem('stream_completer_test', StreamCompleterTestPage('stream_completer_test')),
+ MainItem('stream_extensions_test', StreamExtensionsTestPage('stream_extensions_test')),
+ MainItem('stream_group_test', StreamGroupTestPage('stream_group_test')),
+ MainItem('stream_queue_test', StreamQueueTestPage('stream_queue_test')),
+ MainItem('stream_sink_completer_test', StreamSinkCompleterTestPage('stream_sink_completer_test')),
+ MainItem('stream_sink_transformer_test', StreamSinkTransformerTestPage('stream_sink_transformer_test')),
+ MainItem('stream_splitter_test', StreamSplitterTestPage('stream_splitter_test')),
+ MainItem('stream_zip_test', StreamZipTestPage('stream_zip_test')),
+ MainItem('stream_zip_zone_test', StreamZipZoneTestPage('stream_zip_zone_test')),
+ MainItem('subscription_stream_test', SubscriptionStreamTestPage('subscription_stream_test')),
+ MainItem('subscription_transformer_test', SubscriptionTransformerTestPage('subscription_transformer_test')),
+ ];
+
+ runApp(TestModelApp(
+ appName: 'async',
+ data: app));
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/AsyncCacheTestPage.dart b/ohos/async_test/lib/src/AsyncCacheTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..109189ebdf7befdae67d5e4b7ef87f7989a52c09
--- /dev/null
+++ b/ohos/async_test/lib/src/AsyncCacheTestPage.dart
@@ -0,0 +1,196 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:fake_async/fake_async.dart';
+
+import '../common/test_page.dart';
+
+class AsyncCacheTestPage extends TestPage {
+
+ AsyncCacheTestPage(super.title) {
+ late AsyncCache cache;
+ setUp() {
+ cache = AsyncCache(const Duration(hours: 1));
+ }
+
+ test('AsyncCache.fetch应在不存在缓存时通过回调获取', () async {
+ setUp();
+ expect(await cache.fetch(() async => 'Expensive'));
+ });
+
+ test('AsyncCache.fetch存在缓存时不应通过回调获取', () async {
+ setUp();
+ await cache.fetch(() async => 'Expensive');
+ expect(await cache.fetch(() async => 'fake'));
+ });
+
+ test('AsyncCache.ephemeral().fetch()不应在未来飞行时通过回调获取', () async {
+ cache = AsyncCache.ephemeral();
+
+ var completer = Completer();
+ expect(cache.fetch(() => completer.future));
+ expect(cache.fetch(() async => 'fake'));
+ completer.complete('Expensive');
+ });
+
+ test('AsyncCache.ephemeral().fetch()应该在飞行中的未来完成时通过回调获取', () async {
+ cache = AsyncCache.ephemeral();
+
+ var fetched = cache.fetch(() async => 'first');
+ expect(fetched);
+ expect(cache.fetch(() async => 'not called'));
+ await fetched;
+ expect(cache.fetch(() async => 'second'));
+ });
+
+ test('AsyncCache.ephemeral().fetch()即使将来抛出异常也应失效', () async {
+ cache = AsyncCache.ephemeral();
+
+ Future throwingCall() async => throw Exception();
+ expect(cache.fetch(throwingCall));
+
+ await Future.delayed(Duration(milliseconds: 5));
+
+ Future call() async => 'Completed';
+ expect(await cache.fetch(call));
+ });
+
+
+ test('AsyncCache(const Duration(hours: 1)).fetch()缓存过期时应再次通过回调获取', () {
+ setUp();
+ FakeAsync().run((fakeAsync) async {
+ var timesCalled = 0;
+ Future call() async => 'Called ${++timesCalled}';
+ expect(cache.fetch(call));
+ expect(cache.fetch(call));
+
+ fakeAsync.elapse(const Duration(hours: 1) - const Duration(seconds: 1));
+ expect(cache.fetch(call));
+
+ fakeAsync.elapse(const Duration(seconds: 1));
+ expect(cache.fetch(call));
+ expect(cache.fetch(call));
+
+ fakeAsync.elapse(const Duration(hours: 1));
+ expect(cache.fetch(call));
+ });
+ FakeAsync();
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetch()应在手动无效时通过回调获取', () async {
+ setUp();
+ var timesCalled = 0;
+ Future call() async => 'Called ${++timesCalled}';
+ expect(await cache.fetch(call));
+ cache.invalidate();
+ expect(await cache.fetch(call));
+ cache.invalidate();
+ expect(await cache.fetch(call));
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetchStream()应该通过回调获取流', () async {
+ setUp();
+ expect(
+ await cache.fetchStream(() {
+ return Stream.fromIterable(['1', '2', '3']);
+ }).toList());
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetchStream()存在缓存时不应通过回调获取流', () async {
+ setUp();
+ await cache.fetchStream(() async* {
+ yield '1';
+ yield '2';
+ yield '3';
+ }).toList();
+ expect(
+ await cache.fetchStream(Stream.empty).toList());
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetchStream()在飞行中请求时不应通过回调获取流', () async {
+ setUp();
+ var controller = StreamController();
+ Stream call() => controller.stream;
+ expect(cache.fetchStream(call).toList());
+ controller.add('1');
+ controller.add('2');
+ await Future.value();
+ expect(cache.fetchStream(call).toList());
+ controller.add('3');
+ await controller.close();
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetchStream()缓存过期时应再次通过回调获取流', () {
+ setUp();
+ FakeAsync().run((fakeAsync) async {
+ var timesCalled = 0;
+ Stream call() {
+ return Stream.fromIterable(['Called ${++timesCalled}']);
+ }
+
+ expect(await cache.fetchStream(call).toList());
+ expect(await cache.fetchStream(call).toList());
+
+ fakeAsync.elapse(const Duration(hours: 1) - const Duration(seconds: 1));
+ expect(await cache.fetchStream(call).toList());
+
+ fakeAsync.elapse(const Duration(seconds: 1));
+ expect(await cache.fetchStream(call).toList());
+ expect(await cache.fetchStream(call).toList());
+
+ fakeAsync.elapse(const Duration(hours: 1));
+ expect(await cache.fetchStream(call).toList());
+ });
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetchStream()应在手动无效时通过回调获取', () async {
+ setUp();
+ var timesCalled = 0;
+ Stream call() {
+ return Stream.fromIterable(['Called ${++timesCalled}']);
+ }
+
+ expect(await cache.fetchStream(call).toList());
+ cache.invalidate();
+ expect(await cache.fetchStream(call).toList());
+ cache.invalidate();
+ expect(await cache.fetchStream(call).toList());
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetchStream()应在不影响其他流的情况下取消缓存流', () async {
+ setUp();
+ Stream call() => Stream.fromIterable(['1', '2', '3']);
+
+ expect(cache.fetchStream(call).toList());
+
+ expect(await cache.fetchStream(call).first);
+ });
+
+ test('AsyncCache(const Duration(hours: 1)).fetchStream()应该暂停缓存的流而不影响其他流', () async {
+ setUp();
+ Stream call() => Stream.fromIterable(['1', '2', '3']);
+
+ late StreamSubscription sub;
+ sub = cache.fetchStream(call).listen((event) {
+ if (event == '1') sub.pause();
+ });
+ expect(cache.fetchStream(call).toList());
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/AsyncMemoizerTestPage.dart b/ohos/async_test/lib/src/AsyncMemoizerTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..8c255e1d3ca5d22054f5a80a9009cb251e9af48c
--- /dev/null
+++ b/ohos/async_test/lib/src/AsyncMemoizerTestPage.dart
@@ -0,0 +1,58 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:async/async.dart';
+
+import '../common/test_page.dart';
+
+class AsyncMemoizerTestPage extends TestPage {
+
+ late AsyncMemoizer cache;
+ setUp() => cache = AsyncMemoizer();
+
+ AsyncMemoizerTestPage(super.title) {
+ test('AsyncMemoizer().runOnce()仅在第一次调用runOnce()时运行函数', () async {
+ setUp();
+ var count = 0;
+ expect(await cache.runOnce(() => count++));
+ expect(count);
+
+ expect(await cache.runOnce(() => count++));
+ expect(count);
+ });
+
+ test('AsyncMemoizer().future, AsyncMemoizer().runOnce()转发函数的返回值', () async {
+ setUp();
+ expect(cache.future);
+ expect(cache.runOnce(() => 'value'));
+ expect(cache.runOnce(() {}));
+ });
+
+ test('AsyncMemoizer().future, AsyncMemoizer().runOnce()转发异步函数的返回值', () async {
+ setUp();
+ expect(cache.future);
+ expect(cache.runOnce(() async => 'value'));
+ expect(cache.runOnce(() {}));
+ });
+
+ test('AsyncMemoizer().future, AsyncMemoizer().runOnce()转发来自异步函数的错误', () async {
+ expect(cache.future);
+ expect(cache.runOnce(() async => throw 'error'));
+ expect(cache.runOnce(() {}));
+ });
+
+ }
+
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/ByteCollectionTestPage.dart b/ohos/async_test/lib/src/ByteCollectionTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..686a45399feda3130a21d250d1bd64179d0839bf
--- /dev/null
+++ b/ohos/async_test/lib/src/ByteCollectionTestPage.dart
@@ -0,0 +1,100 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+
+import '../common/test_page.dart';
+
+class ByteCollectionTestPage extends TestPage {
+ ByteCollectionTestPage(super.title) {
+ test('collectBytes()简单列表和溢出', () {
+ var result = collectBytes(Stream.fromIterable([
+ [0],
+ [1],
+ [2],
+ [256]
+ ]));
+ expect(result);
+ });
+
+ test('collectBytes()无事件', () {
+ var result = collectBytes(Stream.fromIterable([]));
+ expect(result);
+ });
+
+ test('collectBytes()空事件', () {
+ var result = collectBytes(Stream.fromIterable([[], []]));
+ expect(result);
+ });
+
+ test('collectBytes()错误事件', () {
+ var result = collectBytes(Stream.fromIterable(
+ Iterable.generate(3, (n) => n == 2 ? throw 'badness' : [n])));
+ expect(result);
+ });
+
+ test('collectBytesCancelable()简单列表和溢出', () {
+ var result = collectBytesCancelable(Stream.fromIterable([
+ [0],
+ [1],
+ [2],
+ [256]
+ ]));
+ expect(result.value);
+ });
+
+ test('collectBytesCancelable()无事件', () {
+ var result = collectBytesCancelable(Stream.fromIterable([]));
+ expect(result.value);
+ });
+
+ test('collectBytesCancelable()空事件', () {
+ var result = collectBytesCancelable(Stream.fromIterable([[], []]));
+ expect(result.value);
+ });
+
+ test('collectBytesCancelable()错误事件', () {
+ var result = collectBytesCancelable(Stream.fromIterable(
+ Iterable.generate(3, (n) => n == 2 ? throw 'badness' : [n])));
+ expect(result.value);
+ });
+
+ test('collectBytesCancelable().value.whenComplete, collectBytesCancelable().cancel()取消', () async {
+ var sc = StreamController
>();
+ var result = collectBytesCancelable(sc.stream);
+ result.value.whenComplete(() {});
+
+ expect(sc.hasListener);
+ sc.add([1, 2]);
+ await nextTimerTick();
+ expect(sc.hasListener);
+ sc.add([3, 4]);
+ await nextTimerTick();
+ expect(sc.hasListener);
+ result.cancel();
+ expect(sc.hasListener); // Cancelled immediately.
+ var replacement = await result.valueOrCancellation();
+ expect(replacement);
+ await nextTimerTick();
+ sc.close();
+ await nextTimerTick();
+ });
+ }
+
+}
+
+Future nextTimerTick() => Future(() {});
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/CancelableOperationTestPage.dart b/ohos/async_test/lib/src/CancelableOperationTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..a7d11d781a9ca7dc27af33961ea812d70969cda2
--- /dev/null
+++ b/ohos/async_test/lib/src/CancelableOperationTestPage.dart
@@ -0,0 +1,986 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+import 'dart:math';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+class CancelableOperationTestPage extends TestPage {
+ CancelableOperationTestPage(super.title) {
+ group('CancelableCompleter而不会被取消', () {
+ late CancelableCompleter completer;
+ setUp() {
+ completer = CancelableCompleter(onCancel: () {});
+ }
+
+ test(
+ 'CancelableCompleter(onCancel: () {}).operation.value, .isCompleted, .complete()向未来发送值',
+ () {
+ setUp();
+ expect(completer.operation.value);
+ expect(completer.isCompleted);
+ completer.complete(1);
+ expect(completer.isCompleted);
+ });
+
+ test('将空值发送到未来', () {
+ setUp();
+ expect(completer.operation.value);
+ expect(completer.isCompleted);
+ completer.complete(null);
+ expect(completer.isCompleted);
+ });
+
+ test('将错误发送到未来', () {
+ setUp();
+ expect(completer.operation.value);
+ expect(completer.isCompleted);
+ completer.completeError('error');
+ expect(completer.isCompleted);
+ });
+
+ test('将未来的值发送到未来', () {
+ setUp();
+ expect(completer.operation.value);
+ expect(completer.isCompleted);
+ completer.complete(Future.value(1));
+ expect(completer.isCompleted);
+ });
+
+ test('将将来的错误发送到将来', () async {
+ setUp();
+ expect(completer.operation.value);
+ expect(completer.isCompleted);
+ expect(completer.operation.isCompleted);
+ completer.complete(Future.error('error'));
+ expect(completer.isCompleted);
+ await flushMicrotasks();
+ expect(completer.operation.isCompleted);
+ });
+
+ test(
+ 'CancelableCompleter(onCancel: () {}).completeOperation(CancelableOperation.fromFuture(Future.value(1)))将可取消操作的值发送到未来',
+ () {
+ setUp();
+ expect(completer.operation.value);
+ completer
+ .completeOperation(CancelableOperation.fromFuture(Future.value(1)));
+ });
+
+ test('将已完成的可取消操作中的值发送到未来', () async {
+ setUp();
+ final operation = CancelableOperation.fromFuture(Future.value(1));
+ await operation.value;
+ expect(completer.operation.value);
+ completer.completeOperation(operation);
+ });
+
+ test('将可取消操作中的错误发送到未来', () {
+ setUp();
+ expect(completer.operation.value);
+ completer.completeOperation(
+ CancelableOperation.fromFuture(Future.error('error')..ignore()));
+ });
+
+ test('将已完成的可取消操作中的错误发送到未来', () async {
+ setUp();
+ final operation =
+ CancelableOperation.fromFuture(Future.error('error')..ignore());
+ try {
+ await operation.value;
+ } on Object {
+ // ignore
+ }
+ expect(completer.operation.value);
+ completer.completeOperation(operation);
+ });
+
+ test(
+ 'CancelableCompleter(onCancel: () {}).operation.valueOrCancellation()将值发送到valueOrCancellation',
+ () {
+ setUp();
+ expect(completer.operation.valueOrCancellation());
+ completer.complete(1);
+ });
+
+ test('将错误发送到valueOrCancellation', () {
+ setUp();
+ expect(completer.operation.valueOrCancellation());
+ completer.completeError('error');
+ });
+
+ test(
+ 'CancelableOperation.fromFuture(Future.value(null)).then((_) {}).value)',
+ () async {
+ setUp();
+ var operation = CancelableOperation.fromFuture(Future.value(null));
+ expect(await operation.then((_) {}).value);
+ });
+
+ test('在结果可用之前不完整', () async {
+ var backingWork = Completer();
+ var operation = CancelableOperation.fromFuture(backingWork.future);
+ expect(operation.isCompleted);
+ backingWork.complete();
+ await backingWork.future;
+ expect(operation.isCompleted);
+ });
+
+ test('CancelableCompleter(onCancel: () {}).complete()成功两次', () {
+ setUp();
+ completer.complete(1);
+ expect(() => completer.complete(1));
+ });
+
+ test('成功然后失败', () {
+ setUp();
+ completer.complete(1);
+ expect(() => completer.completeError('error'));
+ });
+
+ test('两次失败', () {
+ setUp();
+ expect(completer.operation.value);
+ completer.completeError('error');
+ expect(() => completer.completeError('error'));
+ });
+
+ test('成功,然后有了未来', () {
+ setUp();
+ completer.complete(1);
+ expect(() => completer.complete(Completer().future));
+ });
+
+ test('有了未来,就成功了', () {
+ setUp();
+ completer.complete(Completer().future);
+ expect(() => completer.complete(1));
+ });
+
+ test('有两次未来', () {
+ setUp();
+ completer.complete(Completer().future);
+ expect(() => completer.complete(Completer().future));
+ });
+
+ test('放入Future.value()实现远期价值', () {
+ setUp();
+ var operation = CancelableOperation.fromFuture(Future.value(1));
+ expect(operation.value);
+ });
+
+ test('放入Future.error()实现转发错误', () {
+ setUp();
+ var operation = CancelableOperation.fromFuture(Future.error('error'));
+ expect(operation.value);
+ });
+
+ test('CancelableOperation.fromSubscription()完成后转发已完成的事件', () async {
+ setUp();
+ var controller = StreamController();
+ var operationCompleted = false;
+ CancelableOperation.fromSubscription(controller.stream.listen(null))
+ .then((_) {
+ operationCompleted = true;
+ });
+
+ await flushMicrotasks();
+ expect(operationCompleted);
+
+ controller.close();
+ await flushMicrotasks();
+ expect(operationCompleted);
+
+ test('CancelableOperation.fromSubscription()转发错误', () {
+ var operation = CancelableOperation.fromSubscription(
+ Stream.error('error').listen(null));
+ expect(operation.value);
+ });
+ });
+ });
+
+ group('CancelableCompleter取消时', () {
+ test('CancelableCompleter().operation.value未来值不改变', () async {
+ var completer = CancelableCompleter();
+ completer.operation.value.whenComplete(() {});
+ completer.operation.cancel();
+
+ await flushMicrotasks();
+ expect(completer.operation.value);
+ completer.complete();
+ await flushMicrotasks();
+ expect(completer.operation.value);
+ });
+
+ test('设置CancelableCompleter(onCancel: () {})测试取消时的签名变化', () {
+ var canceled = false;
+ late CancelableCompleter completer;
+ completer = CancelableCompleter(onCancel: () {
+ expect(completer.isCanceled);
+ canceled = true;
+ });
+
+ expect(canceled);
+ expect(completer.isCanceled);
+ expect(completer.operation.isCanceled);
+ expect(completer.isCompleted);
+ expect(completer.operation.isCompleted);
+ completer.operation.cancel();
+ expect(canceled);
+ expect(completer.isCanceled);
+ expect(completer.operation.isCanceled);
+ expect(completer.isCompleted);
+ expect(completer.operation.isCompleted);
+ });
+
+ test('每次调用cancel时返回onCancel future', () {
+ var completer = CancelableCompleter(onCancel: () {
+ return Future.value(1);
+ });
+ expect(completer.operation.cancel());
+ expect(completer.operation.cancel());
+ expect(completer.operation.cancel());
+ });
+
+ test("返回future,即使onCancel没有", () {
+ var completer = CancelableCompleter(onCancel: () {});
+ expect(completer.operation.cancel());
+ });
+
+ test("如果完成器已完成,则不调用Cancel", () {
+ var completer = CancelableCompleter(onCancel: () {});
+ completer.complete(1);
+ expect(completer.operation.value);
+ expect(completer.operation.cancel());
+ });
+
+ test('如果完成者已完成未完成的Future,则调用Cancel', () {
+ var completer = CancelableCompleter(onCancel: () {});
+ completer.complete(Completer().future);
+ expect(completer.operation.cancel());
+ });
+
+ test('如果完成器已完成激发的Future,则不调用onCancel', () async {
+ var completer = CancelableCompleter(onCancel: () {});
+ completer.complete(Future.value(1));
+ await completer.operation.value;
+ expect(completer.operation.cancel());
+ });
+
+ test('取消后可以完成一次', () async {
+ var completer = CancelableCompleter();
+ completer.operation.value.whenComplete(() {});
+ await completer.operation.cancel();
+ completer.complete(1);
+ expect(() => completer.complete(1));
+ });
+
+ test('使用给定值激发valueOrCancellation', () {
+ var completer = CancelableCompleter();
+ expect(completer.operation.valueOrCancellation(1));
+ completer.operation.cancel();
+ });
+
+ test('通过valueOrCancellation传递错误', () {
+ var completer = CancelableCompleter(onCancel: () {
+ throw 'error';
+ });
+ expect(completer.operation.valueOrCancellation(1));
+ completer.operation.cancel();
+ });
+
+ test('valueOrCancellation等待onCancel未来', () async {
+ var innerCompleter = Completer();
+ var completer =
+ CancelableCompleter(onCancel: () => innerCompleter.future);
+
+ var fired = false;
+ completer.operation.valueOrCancellation().then((_) {
+ fired = true;
+ });
+
+ completer.operation.cancel();
+ await flushMicrotasks();
+ expect(fired);
+
+ innerCompleter.complete();
+ await flushMicrotasks();
+ expect(fired);
+ });
+
+ test('CancelableOperation.fromSubscription()取消订阅', () async {
+ var cancelCompleter = Completer();
+ var canceled = false;
+ var controller = StreamController(onCancel: () {
+ canceled = true;
+ return cancelCompleter.future;
+ });
+ var operation = CancelableOperation.fromSubscription(
+ controller.stream.listen(null));
+
+ await flushMicrotasks();
+ expect(canceled);
+
+ var cancelCompleted = false;
+ expect(
+ operation.cancel().then((_) {
+ cancelCompleted = true;
+ }));
+ await flushMicrotasks();
+ expect(canceled);
+ expect(cancelCompleted);
+
+ cancelCompleter.complete();
+ await flushMicrotasks();
+ expect(cancelCompleted);
+ });
+
+ test(
+ 'CancelableCompleter().completeOperation(CancelableCompleter().operation..cancel())发送可取消操作的取消',
+ () async {
+ final completer = CancelableCompleter();
+ completer.operation.value.whenComplete(() {});
+ completer
+ .completeOperation(CancelableCompleter().operation..cancel());
+ await completer.operation.valueOrCancellation();
+ expect(completer.operation.isCanceled);
+ });
+
+ test('将已完成的可取消操作中的错误发送到未来', () async {
+ final operation = CancelableCompleter().operation..cancel();
+ await operation.valueOrCancellation();
+ final completer = CancelableCompleter();
+ completer.operation.value.whenComplete(() {});
+ completer.completeOperation(operation);
+ await completer.operation.valueOrCancellation();
+ expect(completer.operation.isCanceled);
+ });
+
+ test('传播消除', () {
+ final completer = CancelableCompleter();
+ final operation = CancelableCompleter(onCancel: () {}).operation;
+ completer.completeOperation(operation);
+ expect(completer);
+ completer.operation.cancel();
+ expect(completer);
+ });
+
+ test('从已取消的完成符传播取消', () async {
+ final completer = CancelableCompleter()..operation.cancel();
+ await completer.operation.valueOrCancellation();
+ final operation = CancelableCompleter(onCancel: () {}).operation;
+ expect(completer);
+ completer.completeOperation(operation);
+ expect(completer);
+ });
+ test('可以禁用取消传播', () {
+ final completer = CancelableCompleter();
+ final operation = CancelableCompleter(onCancel: () {}).operation;
+ completer.completeOperation(operation, propagateCancel: false);
+ expect(completer);
+ completer.operation.cancel();
+ expect(completer);
+ });
+
+ test('取消传播可以从已取消状态禁用已完成', () async {
+ final completer = CancelableCompleter()..operation.cancel();
+ await completer.operation.valueOrCancellation();
+ final operation = CancelableCompleter(onCancel: () {}).operation;
+ expect(completer);
+ completer.completeOperation(operation, propagateCancel: false);
+ expect(completer);
+ });
+ });
+
+ group('CancelableCompleter().operation.asStream()', () {
+ test('CancelableCompleter().operation.asStream().toList()', () {
+ var completer = CancelableCompleter();
+ expect(completer.operation.asStream().toList());
+ completer.complete(1);
+ expect(completer.operation.asStream().toList());
+ });
+
+ test('CancelableCompleter().operation.asStream()发出错误,然后关闭', () {
+ var completer = CancelableCompleter();
+ var queue = StreamQueue(completer.operation.asStream());
+ expect(queue.next);
+ expect(queue.hasNext);
+ completer.completeError('error');
+ expect(queue.next);
+ });
+
+ test('取消订阅时取消完成器', () {
+ var completer = CancelableCompleter(onCancel: () {});
+ var sub = completer.operation.asStream().listen((_) {});
+ completer.operation.value.whenComplete(() {});
+ sub.cancel();
+ expect(completer.isCanceled);
+ });
+ });
+
+ group('CancelableCompleter().operation.then()', () {
+ FutureOr Function(int)? onValue;
+ FutureOr Function(Object, StackTrace)? onError;
+ FutureOr Function()? onCancel;
+ late bool propagateCancel;
+ late CancelableCompleter originalCompleter;
+
+ setUp() {
+ onValue = (_) => 'Fake';
+ onError = (e, s) => 'Fake';
+ onCancel = () => 'Fake';
+ propagateCancel = false;
+ originalCompleter = CancelableCompleter();
+ }
+
+ CancelableOperation runThen() {
+ return originalCompleter.operation.then(onValue!,
+ onError: onError,
+ onCancel: onCancel,
+ propagateCancel: propagateCancel);
+ }
+
+ test('CancelableCompleter().operation.then(onValue) onValue成功完成', () {
+ setUp();
+ onValue = (v) => v.toString();
+
+ expect(runThen().value);
+ originalCompleter.complete(1);
+ expect(runThen().value);
+ });
+
+ test('onValue引发错误', () {
+ setUp();
+ onValue = (_) => throw 'error';
+
+ expect(runThen().value);
+ originalCompleter.complete(1);
+ expect(runThen().value);
+ });
+
+ test('onValue返回引发错误的Future', () {
+ setUp();
+ onValue = (v) => Future.error('error');
+
+ expect(runThen().value);
+ originalCompleter.complete(1);
+ expect(runThen().value);
+ });
+
+ test('返回的操作被取消,propagateCancel=false', () async {
+ setUp();
+ propagateCancel = false;
+
+ runThen().cancel();
+ expect(runThen().value);
+
+ originalCompleter.complete(1);
+ expect(runThen().value);
+ });
+
+ test('onError未设置', () {
+ setUp();
+ onError = null;
+
+ expect(runThen().value);
+ originalCompleter.completeError('error');
+ expect(runThen().value);
+ });
+
+ test('onError成功完成', () {
+ setUp();
+ onError = (e, s) => 'onError caught $e';
+
+ expect(runThen().value);
+ originalCompleter.completeError('error');
+ expect(runThen().value);
+ });
+
+ test('onError引发', () {
+ setUp();
+ onError = (e, s) => throw 'onError caught $e';
+
+ expect(runThen().value);
+ originalCompleter.completeError('error');
+ expect(runThen().value);
+ });
+
+ test('onError返回抛出的Future', () {
+ setUp();
+ onError = (e, s) => Future.error('onError caught $e');
+
+ expect(runThen().value);
+ originalCompleter.completeError('error');
+ expect(runThen().value);
+ });
+
+ test('返回的操作被取消,propagateCancel=false', () async {
+ setUp();
+ propagateCancel = false;
+
+ runThen().cancel();
+ expect(runThen().value);
+
+ originalCompleter.completeError('error');
+ expect(runThen().value);
+ });
+
+ test('onCancel未设置', () async {
+ setUp();
+ onCancel = null;
+
+ final operation = runThen();
+
+ expect(originalCompleter.operation.cancel());
+ expect(operation.isCanceled);
+ });
+
+ test('onCancel取消成功完成', () {
+ setUp();
+ onCancel = () => 'canceled';
+
+ expect(runThen().value);
+ originalCompleter.operation.cancel();
+ expect(runThen().value);
+ });
+
+ test('onCancel引发错误', () {
+ setUp();
+ onCancel = () => throw 'error';
+
+ expect(runThen().value);
+ originalCompleter.operation.cancel();
+ expect(runThen().value);
+ });
+
+ test('onCancel返回引发错误的Future', () {
+ setUp();
+ onCancel = () => Future.error('error');
+
+ expect(runThen().value);
+ originalCompleter.operation.cancel();
+ expect(runThen().value);
+ });
+
+ test('使用future完成后不调用onValue', () async {
+ setUp();
+ onValue = (_) => '';
+ onCancel = null;
+ var operation = runThen();
+ var workCompleter = Completer();
+ originalCompleter.complete(workCompleter.future);
+ var cancelation = originalCompleter.operation.cancel();
+ expect(originalCompleter.isCanceled);
+ workCompleter.complete(0);
+ await cancelation;
+ expect(operation.isCanceled);
+ await workCompleter.future;
+ });
+
+ test('值完成后调用`onValue`', () {
+ setUp();
+ onValue = (_) => 'foo';
+ onCancel = () => '';
+ originalCompleter.complete(0);
+ originalCompleter.operation.cancel();
+ var operation = runThen();
+ expect(operation.value);
+ expect(operation.isCanceled);
+ });
+
+ test('等待连锁取消', () async {
+ setUp();
+ var completer = CancelableCompleter();
+ var chainedOperation = completer.operation
+ .then((_) => Future.delayed(Duration(milliseconds: 1)))
+ .then((_) => Future.delayed(Duration(milliseconds: 1)));
+
+ await completer.operation.cancel();
+ expect(completer.operation.isCanceled);
+ expect(chainedOperation.isCanceled);
+ });
+
+ test('propagateCancel = true', () async {
+ setUp();
+ propagateCancel = true;
+
+ await runThen().cancel();
+
+ expect(originalCompleter.isCanceled);
+ });
+
+ test('propagateCancel = false', () async {
+ setUp();
+ propagateCancel = false;
+
+ await runThen().cancel();
+
+ expect(originalCompleter.isCanceled);
+ });
+
+ test('取消后未调用onValue回调', () async {
+ setUp();
+ var called = false;
+ onValue = (_) {
+ called = true;
+ return 'onValue unreachable';
+ };
+
+ await runThen().cancel();
+ originalCompleter.complete(0);
+ await flushMicrotasks();
+ expect(called);
+ });
+
+ test('取消后未调用onError回调', () async {
+ setUp();
+ var called = false;
+ onError = (_, __) {
+ called = true;
+ return 'onError unreachable';
+ };
+
+ await runThen().cancel();
+ originalCompleter.completeError('Error', StackTrace.empty);
+ await flushMicrotasks();
+ expect(called);
+ });
+
+ test('取消后未调用onCancel回调', () async {
+ setUp();
+ var called = false;
+ onCancel = () {
+ called = true;
+ return 'onCancel unreachable';
+ };
+
+ await runThen().cancel();
+ await originalCompleter.operation.cancel();
+ await flushMicrotasks();
+ expect(called);
+ });
+ });
+
+ group('CancelableCompleter().operation.thenOperation()', () {
+ late void Function(int, CancelableCompleter) onValue;
+ void Function(Object, StackTrace, CancelableCompleter)? onError;
+ void Function(CancelableCompleter)? onCancel;
+ late bool propagateCancel;
+ late CancelableCompleter originalCompleter;
+
+ setUp() {
+ onValue = (value, completer) => completer.complete('$value');
+ onError = null;
+ onCancel = null;
+ propagateCancel = false;
+ originalCompleter = CancelableCompleter();
+ }
+
+ CancelableOperation runThenOperation() {
+ return originalCompleter.operation.thenOperation(onValue,
+ onError: onError,
+ onCancel: onCancel,
+ propagateCancel: propagateCancel);
+ }
+
+ test('onValue成功完成', () {
+ setUp();
+ onValue = (v, c) => c.complete('$v');
+
+ expect(runThenOperation().value);
+ originalCompleter.complete(1);
+ expect(runThenOperation().value);
+ });
+
+ test('onValue引发错误', () {
+ setUp();
+ onValue = (_, __) => throw 'error';
+
+ expect(runThenOperation().value);
+ originalCompleter.complete(1);
+ expect(runThenOperation().value);
+ });
+
+ test('onValue作为错误完成操作', () {
+ setUp();
+ onValue = (_, completer) => completer.completeError('error');
+
+ expect(runThenOperation().value);
+ originalCompleter.complete(1);
+ expect(runThenOperation().value);
+ });
+
+ test('onValue返回引发错误的Future', () {
+ setUp();
+ onValue = (_, completer) => Future.error('error');
+
+ expect(runThenOperation().value);
+ originalCompleter.complete(1);
+ expect(runThenOperation().value);
+ });
+
+ test('返回的操作被取消', () async {
+ setUp();
+ onValue = (_, __) => throw 'never called';
+ runThenOperation().cancel();
+ expect(runThenOperation().value);
+ originalCompleter.complete(1);
+ expect(runThenOperation().value);
+ });
+
+ test('onError未设置', () {
+ setUp();
+ onError = null;
+
+ expect(runThenOperation().value);
+ originalCompleter.completeError('error');
+ expect(runThenOperation().value);
+ });
+
+ test('onError完成操作', () {
+ setUp();
+ onError = (e, s, c) => c.complete('onError caught $e');
+
+ expect(runThenOperation().value);
+ originalCompleter.completeError('error');
+ expect(runThenOperation().value);
+ });
+
+ test('onError引发', () {
+ setUp();
+ onError = (e, s, c) => throw 'onError caught $e';
+
+ expect(runThenOperation().value);
+ originalCompleter.completeError('error');
+ expect(runThenOperation().value);
+ });
+
+ test('onError返回引发错误的Future', () {
+ setUp();
+ onError = (e, s, c) => Future.error('onError caught $e');
+
+ expect(runThenOperation().value);
+ originalCompleter.completeError('error');
+ expect(runThenOperation().value);
+ });
+
+ test('onError作为错误完成操作', () {
+ setUp();
+ onError = (e, s, c) => c.completeError('onError caught $e');
+
+ expect(runThenOperation().value);
+ originalCompleter.completeError('error');
+ expect(runThenOperation().value);
+ });
+
+ test('返回的操作被取消,propagateCancel=false', () async {
+ setUp();
+ onError = (e, s, c) {};
+
+ runThenOperation().cancel();
+ expect(runThenOperation().value);
+
+ originalCompleter.completeError('error');
+ expect(runThenOperation().value);
+ });
+
+ test('onCancel未设置', () async {
+ setUp();
+ onCancel = null;
+
+ final operation = runThenOperation();
+
+ expect(originalCompleter.operation.cancel());
+ expect(operation.isCanceled);
+ });
+
+ test('onCancel成功完成', () {
+ setUp();
+ onCancel = (c) => c.complete('canceled');
+
+ expect(runThenOperation().value);
+ originalCompleter.operation.cancel();
+ expect(runThenOperation().value);
+ });
+
+ test('onCancel引发错误', () {
+ setUp();
+ onCancel = (_) => throw 'error';
+
+ expect(runThenOperation().value);
+ originalCompleter.operation.cancel();
+ expect(runThenOperation().value);
+ });
+
+ test('onCancel作为错误完成操作', () {
+ setUp();
+ onCancel = (c) => c.completeError('error');
+
+ expect(runThenOperation().value);
+ originalCompleter.operation.cancel();
+ expect(runThenOperation().value);
+ });
+
+ test('onCancel返回引发错误的Future', () {
+ setUp();
+ onCancel = (c) => Future.error('error');
+
+ expect(runThenOperation().value);
+ originalCompleter.operation.cancel();
+ expect(runThenOperation().value);
+ });
+
+ test('使用future完成后不调用`onValue`', () async {
+ setUp();
+ onValue = (_, __) {};
+ onCancel = null;
+ var operation = runThenOperation();
+ var workCompleter = Completer();
+ originalCompleter.complete(workCompleter.future);
+ var cancelation = originalCompleter.operation.cancel();
+ expect(originalCompleter.isCanceled);
+ workCompleter.complete(0);
+ await cancelation;
+ expect(operation.isCanceled);
+ await workCompleter.future;
+ });
+
+ test('值完成后调用`onValue`', () {
+ setUp();
+ onValue = (v, c) => c.complete('foo');
+ onCancel = (_) {};
+ originalCompleter.complete(0);
+ originalCompleter.operation.cancel();
+ var operation = runThenOperation();
+ expect(operation.value);
+ expect(operation.isCanceled);
+ });
+
+ test('propagateCancel = true', () async {
+ setUp();
+ propagateCancel = true;
+
+ await runThenOperation().cancel();
+
+ expect(originalCompleter.isCanceled);
+ });
+
+ test('propagateCancel = false', () async {
+ setUp();
+ propagateCancel = false;
+
+ await runThenOperation().cancel();
+
+ expect(originalCompleter.isCanceled);
+ });
+
+ test('取消后未调用onValue回调', () async {
+ setUp();
+ onValue = (_, c) {};
+
+ await runThenOperation().cancel();
+ expect(runThenOperation().value);
+ originalCompleter.complete(0);
+ });
+
+ test('取消后未调用onError回调', () async {
+ setUp();
+ onError = (_, __, ___) {};
+
+ await runThenOperation().cancel();
+ expect(runThenOperation().value);
+ originalCompleter.completeError('Error', StackTrace.empty);
+ });
+
+ test('取消后未调用onCancel回调', () async {
+ setUp();
+ onCancel = (_) {};
+
+ await runThenOperation().cancel();
+ expect(runThenOperation().value);
+ await originalCompleter.operation.cancel();
+ });
+ });
+
+ group('CancelableOperation.race()', () {
+ late bool canceled1;
+ late CancelableCompleter completer1;
+ late bool canceled2;
+ late CancelableCompleter completer2;
+ late bool canceled3;
+ late CancelableCompleter completer3;
+ late CancelableOperation operation;
+ setUp() {
+ canceled1 = false;
+ completer1 = CancelableCompleter(onCancel: () {
+ canceled1 = true;
+ });
+
+ canceled2 = false;
+ completer2 = CancelableCompleter(onCancel: () {
+ canceled2 = true;
+ });
+
+ canceled3 = false;
+ completer3 = CancelableCompleter(onCancel: () {
+ canceled3 = true;
+ });
+
+ operation = CancelableOperation.race(
+ [completer1.operation, completer2.operation, completer3.operation]);
+ }
+
+ test('返回要完成的第一个值', () {
+ setUp();
+ completer1.complete(1);
+ completer2.complete(2);
+ completer3.complete(3);
+
+ expect(operation.value);
+ });
+
+ test('抛出要完成的第一个错误', () {
+ setUp();
+ completer1.completeError('error 1');
+ completer2.completeError('error 2');
+ completer3.completeError('error 3');
+
+ expect(operation.value);
+ });
+
+ test('取消所有尚未完成的完成符', () async {
+ setUp();
+ completer1.complete(1);
+ expect(operation.value);
+ expect(canceled1);
+ expect(canceled2);
+ expect(canceled3);
+ });
+
+ test('CancelableOperation.race().cancel()操作完成后取消所有完成符', () async {
+ setUp();
+ await operation.cancel();
+
+ expect(canceled1);
+ expect(canceled2);
+ expect(canceled3);
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/ChunkedStreamReaderTestPage.dart b/ohos/async_test/lib/src/ChunkedStreamReaderTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..4162905540accdff528b5ab13a6dc61b7602ced6
--- /dev/null
+++ b/ohos/async_test/lib/src/ChunkedStreamReaderTestPage.dart
@@ -0,0 +1,489 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+import 'dart:typed_data';
+
+import 'package:async/async.dart';
+
+import '../common/test_page.dart';
+
+class ChunkedStreamReaderTestPage extends TestPage {
+ ChunkedStreamReaderTestPage(super.title) {
+ test('readChunk()一块一块', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(4));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk()逐个元素', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ for (var i = 0; i < 10; i++) {
+ expect(await r.readChunk(1));
+ }
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk()逐个元素', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(10));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk()过期', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(20));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk()由2个元素组成的块', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk()由3个元素组成的块', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk()中途取消', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(5));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk()传播异常', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ throw Exception('stopping here');
+ }());
+
+ expect(await r.readChunk(3));
+ expect(r.readChunk(3));
+
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readStream()转发块', () async {
+ final chunk2 = [3, 4, 5];
+ final chunk3 = [6, 7, 8, 9];
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield chunk2;
+ yield chunk3;
+ yield [10];
+ }());
+
+ expect(await r.readChunk(1));
+ final i = StreamIterator(r.readStream(9));
+ expect(await i.moveNext());
+ expect(i.current);
+
+ expect(await i.moveNext());
+ expect(i.current);
+ expect(i.current == chunk2);
+
+ expect(await i.moveNext());
+ expect(i.current);
+ expect(i.current == chunk3);
+
+ expect(await i.moveNext());
+ expect(i.current);
+ expect(await i.moveNext());
+
+ expect(await r.readChunk(1));
+ await r.cancel(); // check this is okay!
+ expect(await r.readChunk(1));
+ });
+
+ test('readStream()最后取消', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(1));
+ final i = StreamIterator(r.readStream(7));
+ expect(await i.moveNext());
+ expect(i.current);
+
+ expect(await i.moveNext());
+ expect(i.current);
+
+ expect(await i.moveNext());
+ expect(i.current);
+
+ await i.cancel();
+
+ expect(await r.readChunk(2));
+
+ expect(await r.readChunk(1));
+ await r.cancel(); // check this is okay!
+ expect(await r.readChunk(1));
+ });
+
+ test('readStream()在区块边界的确切末端取消', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(1));
+ final i = StreamIterator(r.readStream(8));
+ expect(await i.moveNext());
+ expect(i.current);
+
+ expect(await i.moveNext());
+ expect(i.current);
+
+ expect(await i.moveNext());
+ expect(i.current);
+
+ await i.cancel();
+
+ expect(await r.readChunk(2));
+
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readStream()取消时已耗尽', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(1));
+ final i = StreamIterator(r.readStream(7));
+ expect(await i.moveNext());
+ expect(i.current);
+ await i.cancel();
+
+ expect(await r.readChunk(2));
+
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readStream()禁止并发读取', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(1));
+ r.readStream(7);
+
+ expect(await r.readChunk(2));
+ });
+
+ test('readStream()支撑排水', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(1));
+ await r.readStream(7).drain();
+ expect(await r.readChunk(2));
+
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('嵌套ChunkedStreamReader()', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readChunk(1));
+ final r2 = ChunkedStreamReader(r.readStream(7));
+ expect(await r2.readChunk(2));
+ expect(await r2.readChunk(1));
+ await r2.cancel();
+
+ expect(await r.readChunk(2));
+
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('ChunkedStreamReader().readBytes()由3个元素组成的块', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2];
+ yield [3, 4, 5];
+ yield [6, 7, 8, 9];
+ yield [10];
+ }());
+
+ expect(await r.readBytes(3));
+ expect(await r.readBytes(3));
+ expect(await r.readBytes(3));
+ expect(await r.readBytes(3));
+ expect(await r.readBytes(1));
+ expect(await r.readBytes(1));
+ await r.cancel();
+ expect(await r.readBytes(1));
+ });
+
+ test('readChunk()直到流完全结束', () async {
+ final stream = Stream.fromIterable(Iterable.generate(
+ 10,
+ (_) => Uint8List(512),
+ ));
+
+ final r = ChunkedStreamReader(stream);
+ while (true) {
+ final c = await r.readBytes(1024);
+ if (c.isEmpty) {
+ expect(c);
+ break;
+ }
+ }
+ });
+
+ test('在readChunk()挂起时取消', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2, 3];
+ await Completer().future;
+ yield [4];
+ fail('unreachable!');
+ }());
+
+ expect(await r.readBytes(2));
+
+ final future = r.readChunk(2);
+
+ await Future.microtask(() => null);
+ r.cancel();
+
+ expect(await future);
+ });
+
+ test('在readStream()挂起时取消', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield [1, 2, 3];
+ await Completer().future;
+ yield [4];
+ fail('unreachable!');
+ }());
+
+ expect(await collectBytes(r.readStream(2)));
+
+ final stream = r.readStream(2);
+
+ await Future.microtask(() => null);
+ r.cancel();
+
+ expect(await collectBytes(stream));
+ });
+
+ test('readChunk() 一块一块 (Uint8List)', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield Uint8List.fromList([1, 2]);
+ yield Uint8List.fromList([3, 4, 5]);
+ yield Uint8List.fromList([6, 7, 8, 9]);
+ yield Uint8List.fromList([10]);
+ }());
+
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(4));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk() 逐个元素 (Uint8List)', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield Uint8List.fromList([1, 2]);
+ yield Uint8List.fromList([3, 4, 5]);
+ yield Uint8List.fromList([6, 7, 8, 9]);
+ yield Uint8List.fromList([10]);
+ }());
+
+ for (var i = 0; i < 10; i++) {
+ expect(await r.readChunk(1));
+ }
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk() 精确元素 (Uint8List)', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield Uint8List.fromList([1, 2]);
+ yield Uint8List.fromList([3, 4, 5]);
+ yield Uint8List.fromList([6, 7, 8, 9]);
+ yield Uint8List.fromList([10]);
+ }());
+
+ expect(await r.readChunk(10));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk() 过期 (Uint8List)', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield Uint8List.fromList([1, 2]);
+ yield Uint8List.fromList([3, 4, 5]);
+ yield Uint8List.fromList([6, 7, 8, 9]);
+ yield Uint8List.fromList([10]);
+ }());
+
+ expect(await r.readChunk(20));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk() 由2个元素组成的块 (Uint8List)', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield Uint8List.fromList([1, 2]);
+ yield Uint8List.fromList([3, 4, 5]);
+ yield Uint8List.fromList([6, 7, 8, 9]);
+ yield Uint8List.fromList([10]);
+ }());
+
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(2));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+
+ test('readChunk() 由3个元素组成的块 (Uint8List)', () async {
+ final r = ChunkedStreamReader(() async* {
+ yield Uint8List.fromList([1, 2]);
+ yield Uint8List.fromList([3, 4, 5]);
+ yield Uint8List.fromList([6, 7, 8, 9]);
+ yield Uint8List.fromList([10]);
+ }());
+
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(3));
+ expect(await r.readChunk(1));
+ expect(await r.readChunk(1));
+ await r.cancel();
+ expect(await r.readChunk(1));
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/FutureGroupTestPage.dart b/ohos/async_test/lib/src/FutureGroupTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c365e40271473efdc90157ebac3f71231d402f8b
--- /dev/null
+++ b/ohos/async_test/lib/src/FutureGroupTestPage.dart
@@ -0,0 +1,244 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+class FutureGroupTestPage extends TestPage {
+ FutureGroupTestPage(super.title) {
+ late FutureGroup futureGroup;
+ setUp() {
+ futureGroup = FutureGroup();
+ }
+
+ group('FutureGroup没有未来', () {
+ test('FutureGroup.future.then()如果什么都不发生,就永远不会完成', () async {
+ setUp();
+ var completed = false;
+ futureGroup.future.then((_) => completed = true);
+
+ await flushMicrotasks();
+ expect(completed);
+ });
+
+ test("关闭后完成", () {
+ setUp();
+ expect(futureGroup.future);
+ expect(futureGroup.isClosed);
+ futureGroup.close();
+ expect(futureGroup.isClosed);
+ });
+ });
+
+ group('FutureGroup未来已经完成', () {
+ test('如果什么都不发生,就永远不会完成', () async {
+ setUp();
+ futureGroup.add(Future.value());
+ await flushMicrotasks();
+
+ var completed = false;
+ futureGroup.future.then((_) => completed = true);
+
+ await flushMicrotasks();
+ expect(completed);
+ });
+
+ test("关闭后完成", () async {
+ setUp();
+ futureGroup.add(Future.value());
+ await flushMicrotasks();
+
+ expect(futureGroup.future);
+ expect(futureGroup.isClosed);
+ futureGroup.close();
+ expect(futureGroup.isClosed);
+ });
+
+ test("达到未来的价值", () {
+ setUp();
+ futureGroup.add(Future.value(1));
+ futureGroup.close();
+ expect(futureGroup.future);
+ });
+
+ test("完成未来的错误,即使它没有关闭", () {
+ setUp();
+ futureGroup.add(Future.error('error'));
+ expect(futureGroup.future);
+ });
+ });
+
+ test('futureGroup.future.then()完成所有包含的未来', () async {
+ setUp();
+ var completer1 = Completer();
+ var completer2 = Completer();
+ var completer3 = Completer();
+
+ futureGroup.add(completer1.future);
+ futureGroup.add(completer2.future);
+ futureGroup.add(completer3.future);
+ futureGroup.close();
+
+ var completed = false;
+ futureGroup.future.then((_) => completed = true);
+
+ completer1.complete();
+ await flushMicrotasks();
+ expect(completed);
+
+ completer2.complete();
+ await flushMicrotasks();
+ expect(completed);
+
+ completer3.complete();
+ await flushMicrotasks();
+ expect(completed);
+ });
+
+ test('按加法顺序完成到未来的值', () {
+ setUp();
+ var completer1 = Completer();
+ var completer2 = Completer();
+ var completer3 = Completer();
+
+ futureGroup.add(completer1.future);
+ futureGroup.add(completer2.future);
+ futureGroup.add(completer3.future);
+ futureGroup.close();
+
+ completer3.complete(3);
+ completer2.complete(2);
+ completer1.complete(1);
+ expect(futureGroup.future);
+ });
+
+ test("完成到要发出的第一个错误,即使它没有关闭", () {
+ setUp();
+ var completer1 = Completer();
+ var completer2 = Completer();
+ var completer3 = Completer();
+
+ futureGroup.add(completer1.future);
+ futureGroup.add(completer2.future);
+ futureGroup.add(completer3.future);
+
+ completer2.completeError('error 2');
+ completer1.completeError('error 1');
+ expect(futureGroup.future);
+ });
+
+ group('FutureGroup().onIdle:', () {
+ test('FutureGroup().onIdle, FutureGroup().isIdle当最后一个挂起的future完成时发出事件', () async {
+ setUp();
+ var idle = false;
+ futureGroup.onIdle.listen((_) => idle = true);
+
+ var completer1 = Completer();
+ var completer2 = Completer();
+ var completer3 = Completer();
+
+ futureGroup.add(completer1.future);
+ futureGroup.add(completer2.future);
+ futureGroup.add(completer3.future);
+
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+
+ completer1.complete();
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+
+ completer2.complete();
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+
+ completer3.complete();
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+ });
+
+ test('每次空闲时都会发出一个事件', () async {
+ setUp();
+ var idle = false;
+ futureGroup.onIdle.listen((_) => idle = true);
+
+ var completer = Completer();
+ futureGroup.add(completer.future);
+
+ completer.complete();
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+
+ idle = false;
+ completer = Completer();
+ futureGroup.add(completer.future);
+
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+
+ completer.complete();
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+ });
+
+ test('当组关闭时发出事件', () async {
+ setUp();
+ var idle = false;
+ var onIdleDone = false;
+ var futureFired = false;
+
+ futureGroup.onIdle.listen((_) {
+ expect(futureFired);
+ idle = true;
+ }, onDone: () {
+ expect(idle);
+ expect(futureFired);
+ onIdleDone = true;
+ });
+
+ futureGroup.future.then((_) {
+ expect(idle);
+ expect(onIdleDone);
+ futureFired = true;
+ });
+
+ var completer = Completer();
+ futureGroup.add(completer.future);
+ futureGroup.close();
+
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+
+ completer.complete();
+ await flushMicrotasks();
+ expect(idle);
+ expect(futureGroup.isIdle);
+ expect(futureFired);
+ });
+ });
+ }
+}
diff --git a/ohos/async_test/lib/src/LazyStreamTestPage.dart b/ohos/async_test/lib/src/LazyStreamTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..dd2b0e2fec6247c1f130b896fcb679654ab8a6d7
--- /dev/null
+++ b/ohos/async_test/lib/src/LazyStreamTestPage.dart
@@ -0,0 +1,102 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+class LazyStreamTestPage extends TestPage {
+ LazyStreamTestPage(super.title) {
+ test('LazyStream()在侦听流时调用回调', () async {
+ var callbackCalled = false;
+ var stream = LazyStream(() {
+ callbackCalled = true;
+ return Stream.empty();
+ });
+
+ await flushMicrotasks();
+ expect(callbackCalled);
+
+ stream.listen(null);
+ expect(callbackCalled);
+ });
+
+ test('LazyStream(() => StreamController().stream)转发到同步提供的流', () async {
+ var controller = StreamController();
+ var stream = LazyStream(() => controller.stream);
+
+ var events = [];
+ stream.listen(events.add);
+
+ controller.add(1);
+ await flushMicrotasks();
+ expect(events);
+
+ controller.add(2);
+ await flushMicrotasks();
+ expect(events);
+
+ controller.add(3);
+ await flushMicrotasks();
+ expect(events);
+
+ controller.close();
+ });
+
+ test('转发到异步提供的流', () async {
+ var controller = StreamController();
+ var stream = LazyStream(() async => controller.stream);
+
+ var events = [];
+ stream.listen(events.add);
+
+ controller.add(1);
+ await flushMicrotasks();
+ expect(events);
+
+ controller.add(2);
+ await flushMicrotasks();
+ expect(events);
+
+ controller.add(3);
+ await flushMicrotasks();
+ expect(events);
+
+ controller.close();
+ });
+
+ test("LazyStream(Stream.empty)惰性流不能被多次收听", () {
+ var stream = LazyStream(Stream.empty);
+ expect(stream.isBroadcast);
+
+ stream.listen(null);
+ expect(() => stream.listen(null));
+ expect(() => stream.listen(null));
+ });
+
+ test("无法从回调中侦听惰性流", () {
+ late LazyStream stream;
+ stream = LazyStream(() {
+ expect(() => stream.listen(null));
+ return Stream.empty();
+ });
+ stream.listen(null);
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/NullStreamSinkTestPage.dart b/ohos/async_test/lib/src/NullStreamSinkTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e65d795018ed8b79abd4fd9a4d6b8bb78ae652d6
--- /dev/null
+++ b/ohos/async_test/lib/src/NullStreamSinkTestPage.dart
@@ -0,0 +1,125 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+class NullStreamSinkTestPage extends TestPage {
+ NullStreamSinkTestPage(super.title) {
+ group('NullStreamSink()构造', () {
+ test('NullStreamSink().done默认为已完成的未来', () {
+ var sink = NullStreamSink();
+ expect(sink.done);
+ });
+
+ test('NullStreamSink(done: Completer().future)一个自定义的未来可能会过去', () async {
+ var completer = Completer();
+ var sink = NullStreamSink(done: completer.future);
+
+ var doneFired = false;
+ sink.done.then((_) {
+ doneFired = true;
+ });
+ await flushMicrotasks();
+ expect(doneFired);
+
+ completer.complete();
+ await flushMicrotasks();
+ expect(doneFired);
+ });
+
+ test('NullStreamSink().error传递一个要完成的错误', () {
+ var sink = NullStreamSink.error('oh no');
+ expect(sink.done);
+ });
+ });
+
+ group('NullStreamSink()重要事情', () {
+ test('在关闭前静默地放下', () {
+ var sink = NullStreamSink();
+ sink.add(1);
+ sink.addError('oh no');
+ });
+
+ test('NullStreamSink()关闭后抛出StateErrors', () {
+ var sink = NullStreamSink();
+ expect(sink.close());
+
+ expect(() => sink.add(1));
+ expect(() => sink.addError('oh no'));
+ expect(() => sink.addStream(Stream.empty()));
+ });
+
+ test(
+ 'NullStreamSink().addStream(StreamController(onCancel:(){}).stream)收听流,然后立即取消',
+ () async {
+ var sink = NullStreamSink();
+ var canceled = false;
+ var controller = StreamController(onCancel: () {
+ canceled = true;
+ });
+
+ expect(sink.addStream(controller.stream));
+ await flushMicrotasks();
+ expect(canceled);
+ });
+
+ test('返回取消未来', () async {
+ var completer = Completer();
+ var sink = NullStreamSink();
+ var controller = StreamController(onCancel: () => completer.future);
+
+ var addStreamFired = false;
+ sink.addStream(controller.stream).then((_) {
+ addStreamFired = true;
+ });
+ await flushMicrotasks();
+ expect(addStreamFired);
+
+ completer.complete();
+ await flushMicrotasks();
+ expect(addStreamFired);
+ });
+
+ test('通过addStream从cancel future管道错误', () async {
+ var sink = NullStreamSink();
+ var controller = StreamController(onCancel: () => throw 'oh no');
+ expect(sink.addStream(controller.stream));
+ });
+
+ test('导致事件引发StateErrors,直到将来为null', () async {
+ var sink = NullStreamSink();
+ var future = sink.addStream(Stream.empty());
+ expect(() => sink.add(1));
+ expect(() => sink.addError('oh no'));
+ expect(() => sink.addStream(Stream.empty()));
+
+ await future;
+ sink.add(1);
+ sink.addError('oh no');
+ expect(sink.addStream(Stream.empty()));
+ });
+ });
+
+ test('NullStreamSink.error().close()关闭返回已完成的未来', () {
+ var sink = NullStreamSink.error('oh no');
+ expect(sink.close());
+ });
+ }
+}
diff --git a/ohos/async_test/lib/src/RejectErrorsTestPage.dart b/ohos/async_test/lib/src/RejectErrorsTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..820fdda316aef02d9d264c252806f6301159f7de
--- /dev/null
+++ b/ohos/async_test/lib/src/RejectErrorsTestPage.dart
@@ -0,0 +1,234 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+class RejectErrorsTestPage extends TestPage {
+ RejectErrorsTestPage(super.title) {
+ late StreamController controller;
+ setUp() {
+ controller = StreamController();
+ }
+
+ test('StreamController().sink.rejectErrors()传递数据事件', () {
+ setUp();
+ controller.sink.rejectErrors()
+ ..add(1)
+ ..add(2)
+ ..add(3);
+ expect(controller.stream);
+ });
+
+ test('通过近距离活动', () {
+ setUp();
+ controller.sink.rejectErrors()
+ ..add(1)
+ ..close();
+ expect(controller.stream);
+ });
+
+ test('通过addStream()中的数据事件', () {
+ setUp();
+ controller.sink.rejectErrors().addStream(Stream.fromIterable([1, 2, 3]));
+ expect(controller.stream);
+ });
+
+ test('允许多个addStream()调用', () async {
+ setUp();
+ var transformed = controller.sink.rejectErrors();
+ await transformed.addStream(Stream.fromIterable([1, 2, 3]));
+ await transformed.addStream(Stream.fromIterable([4, 5, 6]));
+ expect(controller.stream);
+ });
+
+ group('StreamController().sink.rejectErrors().addError()', () {
+ test('转发要完成的错误', () {
+ setUp();
+ var transformed = controller.sink.rejectErrors();
+ transformed.addError('oh no');
+ expect(transformed.done);
+ });
+
+ test('关闭底层水槽', () {
+ setUp();
+ var transformed = controller.sink.rejectErrors();
+ transformed.addError('oh no');
+ transformed.done.catchError((_) {});
+
+ expect(controller.stream);
+ });
+
+ test('忽略其他事件', () async {
+ setUp();
+ var transformed = controller.sink.rejectErrors();
+ transformed.addError('oh no');
+ transformed.done.catchError((_) {});
+ expect(controller.stream);
+
+ transformed
+ ..add(1)
+ ..addError('another');
+ await pumpEventQueue();
+ transformed
+ ..add(2)
+ ..addError('yet another');
+ });
+
+ test('取消当前订阅', () async {
+ setUp();
+ var inputCanceled = false;
+ var inputController = StreamController(onCancel: () => inputCanceled = true);
+
+ var transformed = controller.sink.rejectErrors()
+ ..addStream(inputController.stream);
+ inputController.addError('oh no');
+ transformed.done.catchError((_) {});
+
+ await pumpEventQueue();
+ expect(inputCanceled);
+ });
+ });
+
+ group('NullStreamSink(done: Completer().future).rejectErrors()当内部水槽完成以后', () {
+ test('已完成', () async {
+ setUp();
+ var completer = Completer();
+ var transformed = NullStreamSink(done: completer.future).rejectErrors();
+
+ var doneCompleted = false;
+ transformed.done.then((_) => doneCompleted = true);
+ await pumpEventQueue();
+ expect(doneCompleted);
+
+ completer.complete();
+ await pumpEventQueue();
+ expect(doneCompleted);
+ });
+
+ test('一个未完成的addStream()完成', () async {
+ setUp();
+ var completer = Completer();
+ var transformed = NullStreamSink(done: completer.future).rejectErrors();
+
+ var addStreamCompleted = false;
+ transformed
+ .addStream(StreamController().stream)
+ .then((_) => addStreamCompleted = true);
+ await pumpEventQueue();
+ expect(addStreamCompleted);
+
+ completer.complete();
+ await pumpEventQueue();
+ expect(addStreamCompleted);
+ });
+
+ test('未完成的addStream()的订阅被取消', () async {
+ setUp();
+ var completer = Completer();
+ var transformed = NullStreamSink(done: completer.future).rejectErrors();
+
+ var addStreamCancelled = false;
+ transformed.addStream(
+ StreamController(onCancel: () => addStreamCancelled = true).stream);
+ await pumpEventQueue();
+ expect(addStreamCancelled);
+
+ completer.complete();
+ await pumpEventQueue();
+ expect(addStreamCancelled);
+ });
+
+ test('转发一个未完成的addStream()的取消错误', () async {
+ setUp();
+ var completer = Completer();
+ var transformed = NullStreamSink(done: completer.future).rejectErrors();
+
+ expect(
+ transformed.addStream(StreamController(onCancel: () => throw 'oh no').stream));
+ completer.complete();
+ });
+
+ group('NullStreamSink(done: Future.error("oh no")).rejectErrors()', () {
+ test('通过 完成', () async {
+ expect(NullStreamSink(done: Future.error('oh no')).rejectErrors().done);
+ });
+
+ test('通过 关闭', () async {
+ expect(NullStreamSink(done: Future.error('oh no')).rejectErrors().close());
+ });
+ });
+ });
+
+ group('关闭后', () {
+ test('在add()上引发', () {
+ setUp();
+ var sink = controller.sink.rejectErrors()..close();
+ expect(() => sink.add(1));
+ });
+
+ test('在addError()上引发', () {
+ setUp();
+ var sink = controller.sink.rejectErrors()..close();
+ expect(() => sink.addError('oh no'));
+ });
+
+ test('在addStream()上引发', () {
+ setUp();
+ var sink = controller.sink.rejectErrors()..close();
+ expect(() => sink.addStream(Stream.empty()));
+ });
+
+ test('允许关闭', () {
+ setUp();
+ var sink = controller.sink.rejectErrors()..close();
+ sink.close();
+ expect(sink.done);
+ });
+ });
+
+ group('在活动addStream()期间', () {
+ test('在add()上引发', () {
+ setUp();
+ var sink = controller.sink.rejectErrors()
+ ..addStream(StreamController().stream);
+ expect(() => sink.add(1));
+ });
+
+ test('在addError()上引发', () {
+ var sink = controller.sink.rejectErrors()
+ ..addStream(StreamController().stream);
+ expect(() => sink.addError('oh no'));
+ });
+
+ test('在addStream()上引发', () {
+ var sink = controller.sink.rejectErrors()
+ ..addStream(StreamController().stream);
+ expect(() => sink.addStream(Stream.empty()));
+ });
+
+ test('闭合时抛出()', () {
+ var sink = controller.sink.rejectErrors()
+ ..addStream(StreamController().stream);
+ expect(() => sink.close());
+ });
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/RestartableTimerTestPage.dart b/ohos/async_test/lib/src/RestartableTimerTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..7e09512a671688870cdf76261736bb7298db4f58
--- /dev/null
+++ b/ohos/async_test/lib/src/RestartableTimerTestPage.dart
@@ -0,0 +1,125 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'package:async/async.dart';
+import 'package:fake_async/fake_async.dart';
+
+import '../common/test_page.dart';
+
+class RestartableTimerTestPage extends TestPage {
+ RestartableTimerTestPage(super.title) {
+ test('FakeAsync().run((async){ RestartableTimer(); })在持续时间结束后运行回调', () {
+ FakeAsync().run((async) {
+ var fired = false;
+ RestartableTimer(Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(Duration(seconds: 4));
+ expect(fired);
+
+ async.elapse(Duration(seconds: 1));
+ expect(fired);
+ });
+ });
+
+ test("RestartableTimer().cancel()如果计时器被取消,则不运行回调", () {
+ FakeAsync().run((async) {
+ var fired = false;
+ var timer = RestartableTimer(Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(Duration(seconds: 4));
+ expect(fired);
+ timer.cancel();
+
+ async.elapse(Duration(seconds: 4));
+ expect(fired);
+ });
+ });
+
+ test('RestartableTimer().reset()如果计时器在启动前重置,则重置持续时间', () {
+ FakeAsync().run((async) {
+ var fired = false;
+ var timer = RestartableTimer(Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(Duration(seconds: 4));
+ expect(fired);
+ timer.reset();
+
+ async.elapse(Duration(seconds: 4));
+ expect(fired);
+
+ async.elapse(Duration(seconds: 1));
+ expect(fired);
+ });
+ });
+
+ test('如果计时器在触发后重置,则重新运行回调', () {
+ FakeAsync().run((async) {
+ var fired = 0;
+ var timer = RestartableTimer(Duration(seconds: 5), () {
+ fired++;
+ });
+
+ async.elapse(Duration(seconds: 5));
+ expect(fired);
+ timer.reset();
+
+ async.elapse(Duration(seconds: 5));
+ expect(fired);
+ timer.reset();
+
+ async.elapse(Duration(seconds: 5));
+ expect(fired);
+ });
+ });
+
+ test('如果计时器在取消后重置,则运行回调', () {
+ FakeAsync().run((async) {
+ var fired = false;
+ var timer = RestartableTimer(Duration(seconds: 5), () {
+ fired = true;
+ });
+
+ async.elapse(Duration(seconds: 4));
+ expect(fired);
+ timer.cancel();
+
+ async.elapse(Duration(seconds: 4));
+ expect(fired);
+ timer.reset();
+
+ async.elapse(Duration(seconds: 5));
+ expect(fired);
+ });
+ });
+
+ test("如果计时器未重置,则只运行一次回调", () {
+ FakeAsync().run((async) {
+ var fired = 0;
+ RestartableTimer(Duration(seconds: 5), () {
+ fired++;
+ });
+ async.elapse(Duration(seconds: 10));
+ expect(fired);
+ });
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/ResultTestPage.dart b/ohos/async_test/lib/src/ResultTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..29cb28ba6eda69dc20525600e10394a6eb7d58a3
--- /dev/null
+++ b/ohos/async_test/lib/src/ResultTestPage.dart
@@ -0,0 +1,621 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:math';
+
+import 'package:async/async.dart';
+import 'package:stack_trace/stack_trace.dart';
+
+import '../common/test_page.dart';
+
+final someStack = StackTrace.current;
+
+Result res(int n) => Result.value(n);
+
+Result err(int n) => ErrorResult('$n', someStack);
+
+/// Helper function creating an iterable of futures.
+Iterable> futures(int count,
+ {bool Function(int index)? throwWhen}) sync* {
+ for (var i = 0; i < count; i++) {
+ if (throwWhen != null && throwWhen(i)) {
+ yield Future.error('$i', someStack);
+ } else {
+ yield Future.value(i);
+ }
+ }
+}
+
+Result res1(T n) => Result.value(n);
+Result err1(int n) => ErrorResult('$n', someStack);
+
+/// Helper function creating an iterable of results.
+Iterable> results(int count,
+ {bool Function(int index)? throwWhen}) sync* {
+ for (var i = 0; i < count; i++) {
+ if (throwWhen != null && throwWhen(i)) {
+ yield err1(i);
+ } else {
+ yield res1(i);
+ }
+ }
+}
+
+class ResultTestPage extends TestPage {
+ ResultTestPage(super.title) {
+ group('Result.captureAll()', () {
+ test('await Result.captureAll(futures(0))', () async {
+ var all = await Result.captureAll(futures(0));
+ expect(all);
+ });
+
+ test('await Result.captureAll(futures(1))', () async {
+ var all = await Result.captureAll(futures(1));
+ expect(all);
+ });
+
+ test('await Result.captureAll(futures(3))', () async {
+ var all = await Result.captureAll(futures(3));
+ expect(all);
+ });
+
+ test('await Result.captureAll(futures(1, throwWhen: (_) => true))', () async {
+ var all = await Result.captureAll(futures(1, throwWhen: (_) => true));
+ expect(all);
+ });
+
+ test('await Result.captureAll(futures(3, throwWhen: (_) => true))', () async {
+ var all = await Result.captureAll(futures(3, throwWhen: (_) => true));
+ expect(all);
+ });
+
+ test('await Result.captureAll(futures(4, throwWhen: (x) => x.isOdd))', () async {
+ var all = await Result.captureAll(futures(4, throwWhen: (x) => x.isOdd));
+ expect(all);
+ });
+
+ test('完成置换1-2-3', () async {
+ var cs = List.generate(3, (_) => Completer());
+ var all = Result.captureAll(cs.map((c) => c.future));
+ expect(all);
+ await _microTask();
+ cs[0].complete(1);
+ await _microTask();
+ cs[1].complete(2);
+ await _microTask();
+ cs[2].completeError('3', someStack);
+ });
+
+ test('完成置换1-3-2', () async {
+ var cs = List.generate(3, (_) => Completer());
+ var all = Result.captureAll(cs.map((c) => c.future));
+ expect(all);
+ await _microTask();
+ cs[0].complete(1);
+ await _microTask();
+ cs[2].completeError('3', someStack);
+ await _microTask();
+ cs[1].complete(2);
+ });
+
+ test('完成排列2-1-3', () async {
+ var cs = List.generate(3, (_) => Completer());
+ var all = Result.captureAll(cs.map((c) => c.future));
+ expect(all);
+ await _microTask();
+ cs[1].complete(2);
+ await _microTask();
+ cs[0].complete(1);
+ await _microTask();
+ cs[2].completeError('3', someStack);
+ });
+
+ test('完成排列2-3-1', () async {
+ var cs = List.generate(3, (_) => Completer());
+ var all = Result.captureAll(cs.map((c) => c.future));
+ expect(all);
+ await _microTask();
+ cs[1].complete(2);
+ await _microTask();
+ cs[2].completeError('3', someStack);
+ await _microTask();
+ cs[0].complete(1);
+ });
+
+ test('完成置换3-1-2', () async {
+ var cs = List.generate(3, (_) => Completer());
+ var all = Result.captureAll(cs.map((c) => c.future));
+ expect(all);
+ await _microTask();
+ cs[2].completeError('3', someStack);
+ await _microTask();
+ cs[0].complete(1);
+ await _microTask();
+ cs[1].complete(2);
+ });
+
+ test('完成排列3-2-1', () async {
+ var cs = List.generate(3, (_) => Completer());
+ var all = Result.captureAll(cs.map((c) => c.future));
+ expect(all);
+ await _microTask();
+ cs[2].completeError('3', someStack);
+ await _microTask();
+ cs[1].complete(2);
+ await _microTask();
+ cs[0].complete(1);
+ });
+
+ var seed = Random().nextInt(0x100000000);
+ var n = 25;
+ test('randomized #$n seed:${seed.toRadixString(16)}', () async {
+ var cs = List.generate(n, (_) => Completer());
+ var all = Result.captureAll(cs.map((c) => c.future));
+ var rnd = Random(seed);
+ var throwFlags = rnd.nextInt(1 << n);
+ bool throws(int index) => (throwFlags & (1 << index)) != 0;
+ var expected = List.generate(n, (x) => throws(x) ? err(x) : res(x));
+
+ expect(all);
+
+ var completeFunctions = List.generate(n, (i) {
+ var c = cs[i];
+ return () =>
+ throws(i) ? c.completeError('$i', someStack) : c.complete(i);
+ });
+ completeFunctions.shuffle(rnd);
+ for (var i = 0; i < n; i++) {
+ await _microTask();
+ completeFunctions[i]();
+ }
+ });
+
+ test('await Result.captureAll([1])', () async {
+ var all = await Result.captureAll([1]);
+ expect(all);
+ });
+ test('await Result.captureAll([1, 2, 3])', () async {
+ var all = await Result.captureAll([1, 2, 3]);
+ expect(all);
+ });
+
+ test('await Result.captureAll(>[1,Future(() => 2),3,Future.value(4),])', () async {
+ var all = await Result.captureAll(>[
+ 1,
+ Future(() => 2),
+ 3,
+ Future.value(4),
+ ]);
+ expect(all);
+ });
+ test('await Result.captureAll(>[1,Future(() => 2),3,Future(() async => await Future.error("4", someStack)),Future.value(5)])', () async {
+ var all = await Result.captureAll(>[
+ 1,
+ Future(() => 2),
+ 3,
+ Future(() async => await Future.error('4', someStack)),
+ Future.value(5)
+ ]);
+ expect(all);
+ });
+ });
+
+ group('Result.flattenAll()', () {
+ void expectAll(Result result, Result expectation) {
+ if (expectation.isError) {
+ expect(result);
+ } else {
+ expect(result.isValue);
+ expect(result.asValue!);
+ }
+ }
+
+ test('Result.flattenAll(results(0)), res([])', () {
+ expectAll(Result.flattenAll(results(0)), res1([]));
+ });
+ test('Result.flattenAll(results(1)), res([0])', () {
+ expectAll(Result.flattenAll(results(1)), res1([0]));
+ });
+ test('Result.flattenAll(results(1, throwWhen: (_) => true)), err(0)', () {
+ expectAll(Result.flattenAll(results(1, throwWhen: (_) => true)), err(0));
+ });
+ test('Result.flattenAll(results(5)), res1([0, 1, 2, 3, 4])', () {
+ expectAll(Result.flattenAll(results(5)), res1([0, 1, 2, 3, 4]));
+ });
+ test('Result.flattenAll(results(5, throwWhen: (x) => x.isOdd)),err(1)', () {
+ expectAll(Result.flattenAll(results(5, throwWhen: (x) => x.isOdd)),
+ err(1)); // First error is result.
+ });
+ test('Result.flattenAll(results(5, throwWhen: (x) => x == 4)), err(4)', () {
+ expectAll(Result.flattenAll(results(5, throwWhen: (x) => x == 4)), err(4));
+ });
+ });
+
+ group('ResultFuture()', () {
+ test('ResultFuture(Completer().future)', () {
+ Completer completer = Completer();
+ ResultFuture future = ResultFuture(completer.future);
+ expect(future.result);
+ });
+
+ test('Completer().complete(12)成功完成后,结果就是未来的值', () {
+ Completer completer = Completer();
+ ResultFuture future = ResultFuture(completer.future);
+ completer.complete(12);
+
+ expect(future.then((_) => future.result!.asValue!.value));
+ });
+
+ test("after an error completion, result is the future's error", () {
+ Completer completer = Completer();
+ ResultFuture future = ResultFuture(completer.future);
+ var trace = Trace.current();
+ completer.completeError('error', trace);
+
+ return future.catchError((_) {}).then((_) {
+ var error = future.result!.asError!;
+ expect(error.error);
+ expect(error.stackTrace);
+ });
+ });
+ });
+
+ group('Result', () {
+ var stack = Trace.current();
+
+ test('Result.value(42)', () {
+ var result = Result.value(42);
+ expect(result.isValue);
+ expect(result.isError);
+ ValueResult value = result.asValue!;
+ expect(value.value);
+ });
+
+ test('ValueResult(42)', () {
+ Result result = ValueResult(42);
+ expect(result.isValue);
+ expect(result.isError);
+ var value = result.asValue!;
+ expect(value.value);
+ });
+
+ test('Result.error("BAD", stack)', () {
+ var result = Result.error('BAD', stack);
+ expect(result.isValue);
+ expect(result.isError);
+ var error = result.asError!;
+ expect(error.error);
+ expect(error.stackTrace);
+ });
+
+ test('ErrorResult("BAD", stack)', () {
+ var result = ErrorResult('BAD', stack);
+ expect(result.isValue);
+ expect(result.isError);
+ var error = result.asError;
+ expect(error.error);
+ expect(error.stackTrace);
+ });
+
+ test('Result.error("BAD")', () {
+ var result = Result.error('BAD');
+ expect(result.isValue);
+ expect(result.isError);
+ var error = result.asError!;
+ expect(error.error);
+ expect(error.stackTrace);
+ });
+
+ test('ValueResult(42).complete(Completer())', () {
+ Result result = ValueResult(42);
+ var c = Completer();
+ c.future.then((int v) {
+ expect(v);
+ }, onError: (e, s) {
+ fail('Unexpected error');
+ });
+ result.complete(c);
+ });
+
+ test('ErrorResult("BAD", stack).complete(Completer())', () {
+ Result result = ErrorResult('BAD', stack);
+ var c = Completer();
+ c.future.then((bool v) {
+ fail('Unexpected value $v');
+ }).then((_) {}, onError: (e, s) {
+ expect(e);
+ expect(s);
+ });
+ result.complete(c);
+ });
+
+ test('ValueResult(42).addTo(TestSink(onData:))', () {
+ var result = ValueResult(42);
+ EventSink sink = TestSink(onData: (v) {
+ expect(v);
+ });
+ result.addTo(sink);
+ });
+
+ test('ErrorResult("BAD", stack).addTo(TestSink(onError:))', () {
+ Result result = ErrorResult('BAD', stack);
+ EventSink sink = TestSink(onError: (e, s) {
+ expect(e);
+ expect(s);
+ });
+ result.addTo(sink);
+ });
+
+ test('ValueResult(42).asFuture.then', () {
+ Result result = ValueResult(42);
+ result.asFuture.then((int v) {
+ expect(v);
+ }, onError: (e, s) {
+ fail('Unexpected error');
+ });
+ });
+
+ test('ErrorResult("BAD", stack).asFuture.then', () {
+ Result result = ErrorResult('BAD', stack);
+ result.asFuture.then((bool v) {
+ fail('Unexpected value $v');
+ }).then((_) {}, onError: (e, s) {
+ expect(e);
+ expect(s);
+ });
+ });
+
+ test('Future.value(42).capture(value).then', () {
+ var value = Future.value(42);
+ Result.capture(value).then((Result result) {
+ expect(result.isValue);
+ expect(result.isError);
+ var value = result.asValue!;
+ expect(value.value);
+ }, onError: (e, s) {
+ fail('Unexpected error: $e');
+ });
+ });
+
+ test('Future.error("BAD", stack).capture(value).then', () {
+ var value = Future.error('BAD', stack);
+ Result.capture(value).then((Result result) {
+ expect(result.isValue);
+ expect(result.isError);
+ var error = result.asError!;
+ expect(error.error);
+ expect(error.stackTrace);
+ }, onError: (e, s) {
+ fail('Unexpected error: $e');
+ });
+ });
+
+ test('Future>.value(Result.value(42)).release(future).then', () {
+ var future = Future>.value(Result.value(42));
+ Result.release(future).then((v) {
+ expect(v);
+ }, onError: (e, s) {
+ fail('Unexpected error: $e');
+ });
+ });
+
+ test('Future>.value(Result.error("BAD", stack)).release(future).then', () {
+ var future = Future>.value(Result.error('BAD', stack));
+ Result.release(future).then((v) {
+ fail('Unexpected value: $v');
+ }).then((_) {}, onError: (e, s) {
+ expect(e);
+ expect(s);
+ });
+ });
+
+ test('Future>.error("BAD", stack).release(future).then', () {
+ var future = Future>.error('BAD', stack);
+ Result.release(future).then((v) {
+ fail('Unexpected value: $v');
+ }).then((_) {}, onError: (e, s) {
+ expect(e);
+ expect(s);
+ });
+ });
+
+ test('Result.captureStream(StreamController().stream)', () {
+ var c = StreamController();
+ var stream = Result.captureStream(c.stream);
+ var expectedList = Queue.of(
+ [Result.value(42), Result.error('BAD', stack), Result.value(37)]);
+ void listener(Result actual) {
+ expect(expectedList.isEmpty);
+ expectResult(actual, expectedList.removeFirst());
+ }
+
+ stream.listen(listener, onDone: () {}, cancelOnError: true);
+ c.add(42);
+ c.addError('BAD', stack);
+ c.add(37);
+ c.close();
+ });
+
+ test('Result.releaseStream(StreamController>().stream)', () {
+ var c = StreamController>();
+ var stream = Result.releaseStream(c.stream);
+ var events = [
+ Result.value(42),
+ Result.error('BAD', stack),
+ Result.value(37)
+ ];
+
+ var expectedList = Queue.of(events)..add(Result.error('BAD2', stack));
+
+ void dataListener(int v) {
+ expect(expectedList.isEmpty);
+ Result expected = expectedList.removeFirst();
+ expect(expected.isValue);
+ expect(v);
+ }
+
+ void errorListener(error, StackTrace stackTrace) {
+ expect(expectedList.isEmpty);
+ Result expected = expectedList.removeFirst();
+ expect(expected.isError);
+ expect(error);
+ expect(stackTrace);
+ }
+
+ stream.listen(dataListener,
+ onError: errorListener,
+ onDone: () {});
+ for (var result in events) {
+ c.add(result);
+ }
+ c.addError('BAD2', stack);
+ c.close();
+ });
+
+ test('Result.releaseStream(StreamController>().stream).listen()', () {
+ var c = StreamController>();
+ var stream = Result.releaseStream(c.stream);
+ stream.listen((v) {
+ expect(v);
+ }, onError: (e, s) {
+ expect(e);
+ expect(s);
+ }, onDone: () {
+ fail('Unexpected done event');
+ }, cancelOnError: true);
+ c.add(Result.value(42));
+ c.add(Result.error('BAD', stack));
+ c.add(Result.value(37));
+ c.close();
+ });
+
+ test('Result.flatten(Result>.error("BAD", stack))压平误差1', () {
+ var error = Result.error('BAD', stack);
+ var flattened = Result.flatten(Result>.error('BAD', stack));
+ expectResult(flattened, error);
+ });
+
+ test('压平误差2', () {
+ var error = Result.error('BAD', stack);
+ var result = Result>.value(error);
+ var flattened = Result.flatten(result);
+ expectResult(flattened, error);
+ });
+
+ test('Result.flatten(Result>.value(Result.value(42)))压平值', () {
+ var result = Result>.value(Result.value(42));
+ expectResult(Result.flatten(result), Result.value(42));
+ });
+
+ test('ErrorResult("error", stack).handle((error){})处理一元的', () {
+ var result = ErrorResult('error', stack);
+ var called = false;
+ result.handle((error) {
+ called = true;
+ expect(error);
+ });
+ expect(called);
+ });
+
+ test('ErrorResult("error", stack).handle处理二进制', () {
+ var result = ErrorResult('error', stack);
+ var called = false;
+ result.handle((error, stackTrace) {
+ called = true;
+ expect(error);
+ expect(stackTrace);
+ });
+ expect(called);
+ });
+
+ test('ErrorResult("error", stack).handle处理一元和二进制', () {
+ var result = ErrorResult('error', stack);
+ var called = false;
+ result.handle((error, [stackTrace]) {
+ called = true;
+ expect(error);
+ expect(stackTrace);
+ });
+ expect(called);
+ });
+
+ test('ErrorResult("error", stack).handle既不处理一元也不处理二进制', () {
+ var result = ErrorResult('error', stack);
+ expect(() => result.handle(() => fail('unreachable')));
+ expect(() => result.handle((a, b, c) => fail('unreachable')));
+ expect(() => result.handle((a, b, {c}) => fail('unreachable')));
+ expect(() => result.handle((a, {b}) => fail('unreachable')));
+ expect(() => result.handle(({a, b}) => fail('unreachable')));
+ expect(() => result.handle(({a}) => fail('unreachable')));
+ });
+ });
+
+ }
+
+}
+
+Future _microTask() => Future.microtask(() {});
+
+
+void expectResult(Result actual, Result expected) {
+ expect(actual.isValue);
+ expect(actual.isError);
+ if (actual.isValue) {
+ expect(actual.asValue!.value);
+ } else {
+ expect(actual.asError!.error);
+ expect(actual.asError!.stackTrace);
+ }
+}
+
+class TestSink implements EventSink {
+ final void Function(T) onData;
+ final void Function(dynamic, StackTrace) onError;
+ final void Function() onDone;
+
+ TestSink(
+ {this.onData = _nullData,
+ this.onError = _nullError,
+ this.onDone = _nullDone});
+
+ @override
+ void add(T value) {
+ onData(value);
+ }
+
+ @override
+ void addError(Object error, [StackTrace? stack]) {
+ onError(error, stack ?? StackTrace.fromString(''));
+ }
+
+ @override
+ void close() {
+ onDone();
+ }
+
+ static void _nullData(value) {
+ expect('Unexpected sink add: $value');
+ }
+
+ static void _nullError(e, StackTrace s) {
+ expect('Unexpected sink addError: $e');
+ }
+
+ static void _nullDone() {
+ expect('Unepxected sink close');
+ }
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/SingleSubscriptionTransformerTestPage.dart b/ohos/async_test/lib/src/SingleSubscriptionTransformerTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..2da9f5492c90ad3a8d30b76cc5c41e0a2abd19bb
--- /dev/null
+++ b/ohos/async_test/lib/src/SingleSubscriptionTransformerTestPage.dart
@@ -0,0 +1,60 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+class SingleSubscriptionTransformerTestPage extends TestPage {
+ SingleSubscriptionTransformerTestPage(super.title) {
+ test("SingleSubscriptionTransformer()绑定后立即缓冲事件", () async {
+ var controller = StreamController.broadcast();
+ var stream = controller.stream.transform(const SingleSubscriptionTransformer());
+
+ controller.add(1);
+ controller.add(2);
+ await flushMicrotasks();
+
+ expect(stream.toList());
+ await flushMicrotasks();
+
+ controller.add(3);
+ controller.add(4);
+ controller.close();
+ });
+
+ test("SingleSubscriptionTransformer()取消订阅广播流时取消订阅", () async {
+ var canceled = false;
+ var controller = StreamController.broadcast(onCancel: () {
+ canceled = true;
+ });
+ var stream = controller.stream.transform(const SingleSubscriptionTransformer());
+ await flushMicrotasks();
+ expect(canceled);
+
+ var subscription = stream.listen(null);
+ await flushMicrotasks();
+ expect(canceled);
+
+ subscription.cancel();
+ await flushMicrotasks();
+ expect(canceled);
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/ohos/async_test/lib/src/SinkBaseTestPage.dart b/ohos/async_test/lib/src/SinkBaseTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e9e03928bc75c5fd38f26dad248abf83574e70bd
--- /dev/null
+++ b/ohos/async_test/lib/src/SinkBaseTestPage.dart
@@ -0,0 +1,404 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+const int letterA = 0x41;
+
+class SinkBaseTestPage extends TestPage {
+ SinkBaseTestPage(super.title) {
+ group('StreamSinkBase', () {
+ test('StreamSinkBase(onAdd: () {}).add() 将add()转发到onAdd()', () {
+ var sink = _StreamSink(onAdd: (value) {
+ expect(value);
+ });
+ sink.add(123);
+ });
+
+ test('StreamSinkBase(onError: () {}).addError() 将addError()转发到onError()',
+ () {
+ var sink = _StreamSink(onError: (error, [stackTrace]) {
+ expect(error);
+ expect(stackTrace);
+ });
+ sink.addError('oh no', StackTrace.current);
+ });
+
+ test(
+ 'StreamSinkBase(onError: () {}).addStream() 将addStream()转发到onAdd()和onError()',
+ () {
+ var sink = _StreamSink(onAdd: (value) {
+ expect(value);
+ }, onError: (error, [stackTrace]) {
+ expect(error);
+ expect(stackTrace);
+ });
+
+ var controller = StreamController();
+ sink.addStream(controller.stream);
+
+ controller.add(123);
+ controller.addError('oh no', StackTrace.current);
+ });
+
+ test('addStream()在流关闭后返回', () async {
+ var sink = _StreamSink();
+ var controller = StreamController();
+ var addStreamCompleted = false;
+ sink
+ .addStream(controller.stream)
+ .then((_) => addStreamCompleted = true);
+
+ await pumpEventQueue();
+ expect(addStreamCompleted);
+
+ controller.addError('oh no', StackTrace.current);
+ await pumpEventQueue();
+ expect(addStreamCompleted);
+
+ controller.close();
+ await pumpEventQueue();
+ expect(addStreamCompleted);
+ });
+
+ test('StreamSinkBase(onClose: () {}) 将close()转发到onClose()', () {
+ var sink = _StreamSink(onClose: () {});
+ expect(sink.close());
+ });
+
+ test('onClose()只调用一次', () {
+ var sink = _StreamSink(onClose: () {});
+ expect(sink.close());
+ expect(sink.close());
+ expect(sink.close());
+ });
+
+ test('close()的所有调用都返回相同的future', () async {
+ var completer = Completer();
+ var sink = _StreamSink(onClose: () => completer.future);
+
+ var close1Completed = false;
+ sink.close().then((_) => close1Completed = true);
+
+ var close2Completed = false;
+ sink.close().then((_) => close2Completed = true);
+
+ var doneCompleted = false;
+ sink.done.then((_) => doneCompleted = true);
+
+ await pumpEventQueue();
+ expect(close1Completed);
+ expect(close2Completed);
+ expect(doneCompleted);
+
+ completer.complete();
+ await pumpEventQueue();
+ expect(close1Completed);
+ expect(close2Completed);
+ expect(doneCompleted);
+ });
+
+ test(
+ 'StreamSinkBase(onClose: () {}).done.then() done返回一个在close()完成后完成的future',
+ () async {
+ var completer = Completer();
+ var sink = _StreamSink(onClose: () => completer.future);
+
+ var doneCompleted = false;
+ sink.done.then((_) => doneCompleted = true);
+
+ await pumpEventQueue();
+ expect(doneCompleted);
+
+ expect(sink.close());
+ await pumpEventQueue();
+ expect(doneCompleted);
+
+ completer.complete();
+ await pumpEventQueue();
+ expect(doneCompleted);
+ });
+
+ test('StreamSinkBase(onAdd: () {}).addStream()过程中add()引发错误', () {
+ var sink = _StreamSink(onAdd: (_) {});
+ sink.addStream(StreamController().stream);
+ expect(() => sink.add(1));
+ });
+
+ test('StreamSinkBase(onAdd: () {}).addStream()过程中addError()引发错误', () {
+ var sink = _StreamSink(onError: (_, [__]) {});
+ sink.addStream(StreamController().stream);
+ expect(() => sink.addError('oh no'));
+ });
+
+ test('StreamSinkBase(onAdd: () {}).addStream()过程中addStream()引发错误', () {
+ var sink = _StreamSink(onAdd: (_) {});
+ sink.addStream(StreamController().stream);
+ expect(() => sink.addStream(Stream.value(123)));
+ });
+
+ test('StreamSinkBase(onAdd: () {}).addStream()过程中close()引发错误', () {
+ var sink = _StreamSink(onClose: () {});
+ sink.addStream(StreamController().stream);
+ expect(() => sink.close());
+ });
+
+ test('StreamSinkBase(onAdd: () {})关闭后由add()引发错误', () {
+ var sink = _StreamSink(onAdd: (_) {});
+ expect(sink.close());
+ expect(() => sink.add(1));
+ });
+
+ test('StreamSinkBase(onError: () {})关闭后由addError()引发错误', () {
+ var sink = _StreamSink(onError: (_, [__]) {});
+ expect(sink.close());
+ expect(() => sink.addError('oh no'));
+ });
+
+ test('StreamSinkBase(onAdd: () {})关闭后由addStream()引发错误', () {
+ var sink = _StreamSink(onAdd: (_) {});
+ expect(sink.close());
+ expect(() => sink.addStream(Stream.value(123)));
+ });
+ });
+
+ group('IOSinkBase', () {
+ test("IOSinkBase(onAdd: () {}).write()不为空字符串调用add()", () async {
+ int i = 0;
+ var sink = _IOSink(onAdd: (_) { i++; });
+ sink.write('');
+ expect(i);
+ });
+
+ test('IOSinkBase(onAdd: () {}).write()将文本转换为数据并传递给添加', () async {
+ var sink = _IOSink(onAdd: (data) {
+ expect(data);
+ });
+ sink.write('hello');
+ });
+
+ test('IOSinkBase(onAdd: () {}).write()调用Object.toString()', () async {
+ var sink = _IOSink(onAdd: (data) {
+ expect(data);
+ });
+ sink.write(123);
+ });
+
+ test('IOSinkBase(onAdd: () {}).write()尊重编码类型', () async {
+ var sink = _IOSink(
+ onAdd: (data) {
+ expect(data);
+ },
+ encoding: latin1);
+ sink.write('Æ');
+ });
+
+ test('流关闭时写入', () async {
+ var sink = _IOSink(onAdd: (_) {});
+ expect(sink.close());
+ expect(() => sink.write('hello'));
+ });
+
+
+ test('IOSinkBase(onAdd: () {}).writeAll()不为空的可迭代项写入任何内容', () async {
+ int i = 0;
+ var sink = _IOSink(onAdd: (_) { i++; });
+ sink.writeAll([]);
+ expect(i);
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeAll()在iterable中写入每个对象', () async {
+ var chunks = >[];
+ var sink = _IOSink(
+ onAdd: (data) {
+ chunks.add(data);
+ });
+
+ sink.writeAll(['hello', 123]);
+ expect(chunks);
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeAll()在每个对象之间写入分隔符', () async {
+ var chunks = >[];
+ var sink = _IOSink(
+ onAdd: (data) {
+ chunks.add(data);
+ });
+
+ sink.writeAll(['hello', 123], '/');
+ expect(chunks);
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeAll()流关闭时写入', () async {
+ var sink = _IOSink(onAdd: (_) {});
+ expect(sink.close());
+ expect(() => sink.writeAll(['hello']));
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeln()默认情况下只写入换行符', () async {
+ var sink = _IOSink(
+ onAdd: (data) {
+ expect(data);
+ });
+ sink.writeln();
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeln()写入对象,后跟一条换行符', () async {
+ var chunks = >[];
+ var sink = _IOSink(
+ onAdd: (data) {
+ chunks.add(data);
+ });
+ sink.writeln(123);
+
+ expect(chunks);
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeln()流关闭时写入', () async {
+ var sink = _IOSink(onAdd: (_) {});
+ expect(sink.close());
+ expect(() => sink.writeln());
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeCharCode()编写字符代码', () async {
+ var sink = _IOSink(onAdd: (data) {
+ expect(data);
+ });
+ sink.writeCharCode(letterA);
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeCharCode()尊重编码', () async {
+ var sink = _IOSink(
+ onAdd: (data) {
+ expect(data);
+ },
+ encoding: latin1);
+ sink.writeCharCode('Æ'.runes.first);
+ });
+
+ test('IOSinkBase(onAdd: () {}).writeCharCode()流关闭时写入', () async {
+ var sink = _IOSink(onAdd: (_) {});
+ expect(sink.close());
+ expect(() => sink.writeCharCode(letterA));
+ });
+
+ test('IOSinkBase(onFlush: () {}).flush().then()返回在onFlush()完成时完成的future',
+ () async {
+ var completer = Completer();
+ var sink = _IOSink(onFlush: () => completer.future);
+
+ var flushDone = false;
+ sink.flush().then((_) => flushDone = true);
+
+ await pumpEventQueue();
+ expect(flushDone);
+
+ completer.complete();
+ await pumpEventQueue();
+ expect(flushDone);
+ });
+
+ test('IOSinkBase(onFlush: () {}).flush()调用close()后不执行任何操作', () {
+ var sink = _IOSink(onFlush: Future.value);
+ expect(sink.close());
+ expect(sink.flush());
+ });
+
+ test("IOSinkBase(onFlush: () {}).flush()无法在addStream()期间调用", () {
+ var sink = _IOSink(onFlush: Future.value);
+ sink.addStream(StreamController>().stream);
+ expect(() => sink.flush());
+ });
+
+ test('IOSinkBase(onFlush: () {}).flush()锁定接收器,就像正在添加流一样', () {
+ var sink = _IOSink(onFlush: () => Completer().future);
+ sink.flush();
+ expect(() => sink.add([0]));
+ expect(() => sink.addError('oh no'));
+ expect(() => sink.addStream(Stream.empty()));
+ expect(() => sink.flush());
+ expect(() => sink.close());
+ });
+ });
+ }
+}
+
+class _StreamSink extends StreamSinkBase {
+ final void Function(int value) _onAdd;
+ final void Function(Object error, [StackTrace? stackTrace]) _onError;
+ final FutureOr Function() _onClose;
+
+ _StreamSink(
+ {void Function(int value)? onAdd,
+ void Function(Object error, [StackTrace? stackTrace])? onError,
+ FutureOr Function()? onClose})
+ : _onAdd = onAdd ?? ((_) {}),
+ _onError = onError ?? ((_, [__]) {}),
+ _onClose = onClose ?? (() {});
+
+ @override
+ void onAdd(int value) {
+ _onAdd(value);
+ }
+
+ @override
+ void onError(Object error, [StackTrace? stackTrace]) {
+ _onError(error, stackTrace);
+ }
+
+ @override
+ FutureOr onClose() => _onClose();
+}
+
+class _IOSink extends IOSinkBase {
+ final void Function(List value) _onAdd;
+ final void Function(Object error, [StackTrace? stackTrace]) _onError;
+ final FutureOr Function() _onClose;
+ final Future Function() _onFlush;
+
+ _IOSink(
+ {void Function(List value)? onAdd,
+ void Function(Object error, [StackTrace? stackTrace])? onError,
+ FutureOr Function()? onClose,
+ Future Function()? onFlush,
+ Encoding encoding = utf8})
+ : _onAdd = onAdd ?? ((_) {}),
+ _onError = onError ?? ((_, [__]) {}),
+ _onClose = onClose ?? (() {}),
+ _onFlush = onFlush ?? Future.value,
+ super(encoding);
+
+ @override
+ void onAdd(List value) {
+ _onAdd(value);
+ }
+
+ @override
+ void onError(Object error, [StackTrace? stackTrace]) {
+ _onError(error, stackTrace);
+ }
+
+ @override
+ FutureOr onClose() => _onClose();
+
+ @override
+ Future onFlush() => _onFlush();
+}
diff --git a/ohos/async_test/lib/src/StreamCloserTestPage.dart b/ohos/async_test/lib/src/StreamCloserTestPage.dart
new file mode 100644
index 0000000000000000000000000000000000000000..c0e93fe98fa024be1131dc852f42691b10e249b0
--- /dev/null
+++ b/ohos/async_test/lib/src/StreamCloserTestPage.dart
@@ -0,0 +1,222 @@
+/*
+* Copyright (c) 2023 Hunan OpenValley Digital Industry Development Co., Ltd.
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import 'dart:async';
+
+import 'package:async/async.dart';
+import 'package:async_test/src/utils.dart';
+
+import '../common/test_page.dart';
+
+class StreamCloserTestPage extends TestPage {
+ StreamCloserTestPage(super.title) {
+ late StreamCloser closer;
+ setUp() {
+ closer = StreamCloser();
+ }
+
+ group('StreamCloser()当关闭器从未关闭时', () {
+ test('转发数据和已完成的事件', () {
+ setUp();
+ expect(createStream().transform(closer).toList());
+ });
+
+ test('转发错误事件', () {
+ setUp();
+ expect(Stream.error('oh no').transform(closer).toList());
+ });
+
+ test('将广播流转换为广播流', () {
+ setUp();
+ expect(Stream.empty().transform(closer).isBroadcast);
+ });
+
+ test("没有急切地倾听", () {
+ setUp();
+ var controller = StreamController();
+ var transformed = controller.stream.transform(closer);
+ expect(controller.hasListener);
+
+ transformed.listen(null);
+ expect(controller.hasListener);
+ });
+
+ test('转发暂停和恢复', () {
+ setUp();
+ var controller = StreamController();
+ var transformed = controller.stream.transform(closer);
+
+ var subscription = transformed.listen(null);
+ expect(controller.isPaused);
+ subscription.pause();
+ expect(controller.isPaused);
+ subscription.resume();
+ expect(controller.isPaused);
+ });
+
+ test('远期取消', () {
+ setUp();
+ var isCancelled = false;
+ var controller =
+ StreamController(onCancel: () => isCancelled = true);
+ var transformed = controller.stream.transform(closer);
+
+ expect(isCancelled);
+ var subscription = transformed.listen(null);
+ expect(isCancelled);
+ subscription.cancel();
+ expect(isCancelled);
+ });
+
+ test('转发来自取消的错误', () {
+ setUp();
+ var controller = StreamController(onCancel: () => throw 'oh no');
+
+ expect(
+ controller.stream.transform(closer).listen(null).cancel());
+ });
+ });
+
+ group('StreamCloser()当在关闭关闭器StreamCloser().close()之前添加流时', () {
+ test('StreamQueue() 关闭关闭器后,流会发出关闭事件', () async {
+ setUp();
+ var queue = StreamQueue(createStream().transform(closer));
+ expect(queue);
+ expect(queue);
+ expect(closer.close());
+ expect(queue);
+ });
+
+ test('关闭关闭后,内部订阅将被取消', () {
+ setUp();
+ var isCancelled = false;
+ var controller =
+ StreamController(onCancel: () => isCancelled = true);
+
+ expect(controller.stream.transform(closer));
+ expect(closer.close());
+ expect(isCancelled);
+ });
+
+ test('StreamCloser().close()转发StreamSubscription.cancel()中的错误', () {
+ setUp();
+ var controller = StreamController(onCancel: () => throw 'oh no');
+
+ expect(controller.stream.transform(closer));
+ expect(closer.close());
+ });
+
+ test('StreamCloser().closer()即使流已经完成也能工作', () async {
+ setUp();
+ expect(await createStream().transform(closer).toList());
+ expect(closer.close());
+ });
+
+ test('StreamCloser().closer()即使流已经被取消也能工作', () async {
+ setUp();
+ createStream().transform(closer).listen(null).cancel();
+ expect(closer.close());
+ });
+
+ test('输出流立即发出done', () {
+ setUp();
+ var stream = createStream().transform(closer);
+ expect(closer.close());
+ expect(stream);
+ });
+
+ test('如果从不侦听流,则从不侦听基础订阅', () async {
+ setUp();
+ var controller = StreamController(onListen: () {});
+ controller.stream.transform(closer);
+
+ expect(closer.close());
+
+ await pumpEventQueue();
+ });
+
+ test('基础订阅被侦听,然后在流被侦听后被取消', () {
+ setUp();
+ var controller =
+ StreamController(onListen: () {}, onCancel: () {});
+ var stream = controller.stream.transform(closer);
+
+ expect(closer.close());
+
+ stream.listen(null);
+ });
+
+ test('Subscription.cancel()错误被静默忽略', () async {
+ setUp();
+ var controller = StreamController(onCancel: () => throw 'oh no');
+ var stream = controller.stream.transform(closer);
+
+ expect(closer.close());
+
+ stream.listen(null);
+ await pumpEventQueue();
+ });
+ });
+
+ group('StreamCloser()关闭关闭后添加流时', () {
+ test('.transform() 输出流立即发出done', () {
+ setUp();
+ expect(closer.close());
+ expect(createStream().transform(closer));
+ });
+
+ test('如果从不侦听流,则从不侦听基础订阅', () async {
+ setUp();
+ expect(closer.close());
+
+ var controller = StreamController(onListen: () {});
+ controller.stream.transform(closer);
+
+ await pumpEventQueue();
+ });
+
+ test('基础订阅被侦听,然后在流被侦听后被取消', () {
+ setUp();
+ expect(closer.close());
+
+ var controller = StreamController(
+ onListen: () {}, onCancel: () {});
+
+ controller.stream.transform(closer).listen(null);
+ });
+
+ test('Subscription.cancel()错误被静默忽略', () async {
+ setUp();
+ expect(closer.close());
+
+ var controller = StreamController(onCancel: () => throw 'oh no');
+
+ controller.stream.transform(closer).listen(null);
+
+ await pumpEventQueue();
+ });
+ });
+ }
+}
+
+Stream