@@ -388,6 +388,9 @@ class _ResolvedNotoSubset {
388388 final List <CodeunitRange > ranges;
389389
390390 _ResolvedNotoSubset (this .url, this .family, this .ranges);
391+
392+ @override
393+ String toString () => '_ResolvedNotoSubset($family , $url )' ;
391394}
392395
393396_NotoFont _notoSansSC = _NotoFont ('Noto Sans SC' , < CodeunitRange > [
@@ -660,21 +663,64 @@ class FallbackFontDownloadQueue {
660663}
661664
662665class NotoDownloader {
666+ int _debugActiveDownloadCount = 0 ;
667+
668+ /// Returns a future that resolves when there are no pending downloads.
669+ ///
670+ /// Useful in tests to make sure that fonts are loaded before working with
671+ /// text.
672+ Future <void > debugWhenIdle () async {
673+ if (assertionsEnabled) {
674+ // Some downloads begin asynchronously in a microtask or in a Timer.run.
675+ // Let those run before waiting for downloads to finish.
676+ await Future <void >.delayed (Duration .zero);
677+ while (_debugActiveDownloadCount > 0 ) {
678+ await Future <void >.delayed (const Duration (milliseconds: 100 ));
679+ // If we started with a non-zero count and hit zero while waiting, wait a
680+ // little more to make sure another download doesn't get chained after
681+ // the last one (e.g. font file download after font CSS download).
682+ if (_debugActiveDownloadCount == 0 ) {
683+ await Future <void >.delayed (const Duration (milliseconds: 100 ));
684+ }
685+ }
686+ } else {
687+ throw UnimplementedError ();
688+ }
689+ }
690+
663691 /// Downloads the [url] and returns it as a [ByteBuffer] .
664692 ///
665693 /// Override this for testing.
666694 Future <ByteBuffer > downloadAsBytes (String url) {
667- return html.window.fetch (url).then ((dynamic fetchResult) => fetchResult
695+ if (assertionsEnabled) {
696+ _debugActiveDownloadCount += 1 ;
697+ }
698+ final Future <ByteBuffer > result = html.window.fetch (url).then ((dynamic fetchResult) => fetchResult
668699 .arrayBuffer ()
669700 .then <ByteBuffer >((dynamic x) => x as ByteBuffer ));
701+ if (assertionsEnabled) {
702+ result.whenComplete (() {
703+ _debugActiveDownloadCount -= 1 ;
704+ });
705+ }
706+ return result;
670707 }
671708
672709 /// Downloads the [url] and returns is as a [String] .
673710 ///
674711 /// Override this for testing.
675712 Future <String > downloadAsString (String url) {
676- return html.window.fetch (url).then ((dynamic response) =>
713+ if (assertionsEnabled) {
714+ _debugActiveDownloadCount += 1 ;
715+ }
716+ final Future <String > result = html.window.fetch (url).then ((dynamic response) =>
677717 response.text ().then <String >((dynamic x) => x as String ));
718+ if (assertionsEnabled) {
719+ result.whenComplete (() {
720+ _debugActiveDownloadCount -= 1 ;
721+ });
722+ }
723+ return result;
678724 }
679725}
680726
0 commit comments