Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(339)

Side by Side Diff: test/chain/vm_test.dart

Issue 1218903003: Add tests for stack chains in the browser. (Closed) Base URL: git@github.com:dart-lang/stack_trace@master
Patch Set: Created 5 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « test/chain/utils.dart ('k') | test/chain_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // dart2js currently doesn't support chain-capturing. See sdk#15171. 5 // VM chain tests can rely on stronger guarantees about the contents of the
6 @TestOn('vm') 6 // stack traces than dart2js.
7 @TestOn('dart-vm')
7 8
8 import 'dart:async'; 9 import 'dart:async';
9 10
10 import 'package:path/path.dart' as p;
11 import 'package:stack_trace/stack_trace.dart'; 11 import 'package:stack_trace/stack_trace.dart';
12 import 'package:test/test.dart'; 12 import 'package:test/test.dart';
13 13
14 import '../utils.dart';
14 import 'utils.dart'; 15 import 'utils.dart';
15 16
16 void main() { 17 void main() {
17 group('capture() with onError catches exceptions', () { 18 group('capture() with onError catches exceptions', () {
18 test('thrown synchronously', () { 19 test('thrown synchronously', () {
19 return captureFuture(() => throw 'error') 20 return captureFuture(() => throw 'error')
20 .then((chain) { 21 .then((chain) {
21 expect(chain.traces, hasLength(1)); 22 expect(chain.traces, hasLength(1));
22 expect(chain.traces.single.frames.first, 23 expect(chain.traces.single.frames.first,
23 frameMember(startsWith('main'))); 24 frameMember(startsWith('main')));
(...skipping 446 matching lines...) Expand 10 before | Expand all | Expand 10 after
470 } catch (_, stackTrace) { 471 } catch (_, stackTrace) {
471 trace = stackTrace; 472 trace = stackTrace;
472 return new Chain.forTrace(stackTrace); 473 return new Chain.forTrace(stackTrace);
473 } 474 }
474 }); 475 });
475 476
476 expect(chain.traces, hasLength(1)); 477 expect(chain.traces, hasLength(1));
477 expect(chain.traces.first.toString(), 478 expect(chain.traces.first.toString(),
478 equals(new Trace.from(trace).toString())); 479 equals(new Trace.from(trace).toString()));
479 }); 480 });
480
481 group('Chain.parse()', () {
482 test('parses a real Chain', () {
483 return captureFuture(() => inMicrotask(() => throw 'error'))
484 .then((chain) {
485 expect(new Chain.parse(chain.toString()).toString(),
486 equals(chain.toString()));
487 });
488 });
489
490 test('parses an empty string', () {
491 var chain = new Chain.parse('');
492 expect(chain.traces, isEmpty);
493 });
494
495 test('parses a chain containing empty traces', () {
496 var chain = new Chain.parse(
497 '===== asynchronous gap ===========================\n'
498 '===== asynchronous gap ===========================\n');
499 expect(chain.traces, hasLength(3));
500 expect(chain.traces[0].frames, isEmpty);
501 expect(chain.traces[1].frames, isEmpty);
502 expect(chain.traces[2].frames, isEmpty);
503 });
504 });
505
506 test("toString() ensures that all traces are aligned", () {
507 var chain = new Chain([
508 new Trace.parse('short 10:11 Foo.bar\n'),
509 new Trace.parse('loooooooooooong 10:11 Zop.zoop')
510 ]);
511
512 expect(chain.toString(), equals(
513 'short 10:11 Foo.bar\n'
514 '===== asynchronous gap ===========================\n'
515 'loooooooooooong 10:11 Zop.zoop\n'));
516 });
517
518 var userSlashCode = p.join('user', 'code.dart');
519 group('Chain.terse', () {
520 test('makes each trace terse', () {
521 var chain = new Chain([
522 new Trace.parse(
523 'dart:core 10:11 Foo.bar\n'
524 'dart:core 10:11 Bar.baz\n'
525 'user/code.dart 10:11 Bang.qux\n'
526 'dart:core 10:11 Zip.zap\n'
527 'dart:core 10:11 Zop.zoop'),
528 new Trace.parse(
529 'user/code.dart 10:11 Bang.qux\n'
530 'dart:core 10:11 Foo.bar\n'
531 'package:stack_trace/stack_trace.dart 10:11 Bar.baz\n'
532 'dart:core 10:11 Zip.zap\n'
533 'user/code.dart 10:11 Zop.zoop')
534 ]);
535
536 expect(chain.terse.toString(), equals(
537 'dart:core Bar.baz\n'
538 '$userSlashCode 10:11 Bang.qux\n'
539 '===== asynchronous gap ===========================\n'
540 '$userSlashCode 10:11 Bang.qux\n'
541 'dart:core Zip.zap\n'
542 '$userSlashCode 10:11 Zop.zoop\n'));
543 });
544
545 test('eliminates internal-only traces', () {
546 var chain = new Chain([
547 new Trace.parse(
548 'user/code.dart 10:11 Foo.bar\n'
549 'dart:core 10:11 Bar.baz'),
550 new Trace.parse(
551 'dart:core 10:11 Foo.bar\n'
552 'package:stack_trace/stack_trace.dart 10:11 Bar.baz\n'
553 'dart:core 10:11 Zip.zap'),
554 new Trace.parse(
555 'user/code.dart 10:11 Foo.bar\n'
556 'dart:core 10:11 Bar.baz')
557 ]);
558
559 expect(chain.terse.toString(), equals(
560 '$userSlashCode 10:11 Foo.bar\n'
561 '===== asynchronous gap ===========================\n'
562 '$userSlashCode 10:11 Foo.bar\n'));
563 });
564
565 test("doesn't return an empty chain", () {
566 var chain = new Chain([
567 new Trace.parse(
568 'dart:core 10:11 Foo.bar\n'
569 'package:stack_trace/stack_trace.dart 10:11 Bar.baz\n'
570 'dart:core 10:11 Zip.zap'),
571 new Trace.parse(
572 'dart:core 10:11 A.b\n'
573 'package:stack_trace/stack_trace.dart 10:11 C.d\n'
574 'dart:core 10:11 E.f')
575 ]);
576
577 expect(chain.terse.toString(), equals('dart:core E.f\n'));
578 });
579 });
580
581 group('Chain.foldFrames', () {
582 test('folds each trace', () {
583 var chain = new Chain([
584 new Trace.parse(
585 'a.dart 10:11 Foo.bar\n'
586 'a.dart 10:11 Bar.baz\n'
587 'b.dart 10:11 Bang.qux\n'
588 'a.dart 10:11 Zip.zap\n'
589 'a.dart 10:11 Zop.zoop'),
590 new Trace.parse(
591 'a.dart 10:11 Foo.bar\n'
592 'a.dart 10:11 Bar.baz\n'
593 'a.dart 10:11 Bang.qux\n'
594 'a.dart 10:11 Zip.zap\n'
595 'b.dart 10:11 Zop.zoop')
596 ]);
597
598 var folded = chain.foldFrames((frame) => frame.library == 'a.dart');
599 expect(folded.toString(), equals(
600 'a.dart 10:11 Bar.baz\n'
601 'b.dart 10:11 Bang.qux\n'
602 'a.dart 10:11 Zop.zoop\n'
603 '===== asynchronous gap ===========================\n'
604 'a.dart 10:11 Zip.zap\n'
605 'b.dart 10:11 Zop.zoop\n'));
606 });
607
608 test('with terse: true, folds core frames as well', () {
609 var chain = new Chain([
610 new Trace.parse(
611 'a.dart 10:11 Foo.bar\n'
612 'dart:async-patch/future.dart 10:11 Zip.zap\n'
613 'b.dart 10:11 Bang.qux\n'
614 'dart:core 10:11 Bar.baz\n'
615 'a.dart 10:11 Zop.zoop'),
616 new Trace.parse(
617 'a.dart 10:11 Foo.bar\n'
618 'a.dart 10:11 Bar.baz\n'
619 'a.dart 10:11 Bang.qux\n'
620 'a.dart 10:11 Zip.zap\n'
621 'b.dart 10:11 Zop.zoop')
622 ]);
623
624 var folded = chain.foldFrames((frame) => frame.library == 'a.dart',
625 terse: true);
626 expect(folded.toString(), equals(
627 'dart:async Zip.zap\n'
628 'b.dart 10:11 Bang.qux\n'
629 'a.dart Zop.zoop\n'
630 '===== asynchronous gap ===========================\n'
631 'a.dart Zip.zap\n'
632 'b.dart 10:11 Zop.zoop\n'));
633 });
634
635 test('eliminates completely-folded traces', () {
636 var chain = new Chain([
637 new Trace.parse(
638 'a.dart 10:11 Foo.bar\n'
639 'b.dart 10:11 Bang.qux'),
640 new Trace.parse(
641 'a.dart 10:11 Foo.bar\n'
642 'a.dart 10:11 Bang.qux'),
643 new Trace.parse(
644 'a.dart 10:11 Zip.zap\n'
645 'b.dart 10:11 Zop.zoop')
646 ]);
647
648 var folded = chain.foldFrames((frame) => frame.library == 'a.dart');
649 expect(folded.toString(), equals(
650 'a.dart 10:11 Foo.bar\n'
651 'b.dart 10:11 Bang.qux\n'
652 '===== asynchronous gap ===========================\n'
653 'a.dart 10:11 Zip.zap\n'
654 'b.dart 10:11 Zop.zoop\n'));
655 });
656
657 test("doesn't return an empty trace", () {
658 var chain = new Chain([
659 new Trace.parse(
660 'a.dart 10:11 Foo.bar\n'
661 'a.dart 10:11 Bang.qux')
662 ]);
663
664 var folded = chain.foldFrames((frame) => frame.library == 'a.dart');
665 expect(folded.toString(), equals('a.dart 10:11 Bang.qux\n'));
666 });
667 });
668
669 test('Chain.toTrace eliminates asynchronous gaps', () {
670 var trace = new Chain([
671 new Trace.parse(
672 'user/code.dart 10:11 Foo.bar\n'
673 'dart:core 10:11 Bar.baz'),
674 new Trace.parse(
675 'user/code.dart 10:11 Foo.bar\n'
676 'dart:core 10:11 Bar.baz')
677 ]).toTrace();
678
679 expect(trace.toString(), equals(
680 '$userSlashCode 10:11 Foo.bar\n'
681 'dart:core 10:11 Bar.baz\n'
682 '$userSlashCode 10:11 Foo.bar\n'
683 'dart:core 10:11 Bar.baz\n'));
684 });
685
686 group('Chain.track(Future)', () {
687 test('forwards the future value within Chain.capture()', () {
688 Chain.capture(() {
689 expect(Chain.track(new Future.value('value')),
690 completion(equals('value')));
691
692 var trace = new Trace.current();
693 expect(Chain.track(new Future.error('error', trace))
694 .catchError((e, stackTrace) {
695 expect(e, equals('error'));
696 expect(stackTrace.toString(), equals(trace.toString()));
697 }), completes);
698 });
699 });
700
701 test('forwards the future value outside of Chain.capture()', () {
702 expect(Chain.track(new Future.value('value')),
703 completion(equals('value')));
704
705 var trace = new Trace.current();
706 expect(Chain.track(new Future.error('error', trace))
707 .catchError((e, stackTrace) {
708 expect(e, equals('error'));
709 expect(stackTrace.toString(), equals(trace.toString()));
710 }), completes);
711 });
712 });
713
714 group('Chain.track(Stream)', () {
715 test('forwards stream values within Chain.capture()', () {
716 Chain.capture(() {
717 var controller = new StreamController()
718 ..add(1)..add(2)..add(3)..close();
719 expect(Chain.track(controller.stream).toList(),
720 completion(equals([1, 2, 3])));
721
722 var trace = new Trace.current();
723 controller = new StreamController()..addError('error', trace);
724 expect(Chain.track(controller.stream).toList()
725 .catchError((e, stackTrace) {
726 expect(e, equals('error'));
727 expect(stackTrace.toString(), equals(trace.toString()));
728 }), completes);
729 });
730 });
731
732 test('forwards stream values outside of Chain.capture()', () {
733 Chain.capture(() {
734 var controller = new StreamController()
735 ..add(1)..add(2)..add(3)..close();
736 expect(Chain.track(controller.stream).toList(),
737 completion(equals([1, 2, 3])));
738
739 var trace = new Trace.current();
740 controller = new StreamController()..addError('error', trace);
741 expect(Chain.track(controller.stream).toList()
742 .catchError((e, stackTrace) {
743 expect(e, equals('error'));
744 expect(stackTrace.toString(), equals(trace.toString()));
745 }), completes);
746 });
747 });
748 });
749 } 481 }
750
751 /// Runs [callback] in a microtask callback.
752 void inMicrotask(callback()) => scheduleMicrotask(callback);
753
754 /// Runs [callback] in a one-shot timer callback.
755 void inOneShotTimer(callback()) => Timer.run(callback);
756
757 /// Runs [callback] once in a periodic timer callback.
758 void inPeriodicTimer(callback()) {
759 var count = 0;
760 new Timer.periodic(new Duration(milliseconds: 1), (timer) {
761 count++;
762 if (count != 5) return;
763 timer.cancel();
764 callback();
765 });
766 }
767
768 /// Runs [callback] within a long asynchronous Future chain.
769 void inFutureChain(callback()) {
770 new Future(() {})
771 .then((_) => new Future(() {}))
772 .then((_) => new Future(() {}))
773 .then((_) => new Future(() {}))
774 .then((_) => new Future(() {}))
775 .then((_) => callback())
776 .then((_) => new Future(() {}));
777 }
778
779 void inNewFuture(callback()) {
780 new Future(callback);
781 }
782
783 void inSyncFuture(callback()) {
784 new Future.sync(callback);
785 }
786
787 /// Returns a Future that completes to an error using a completer.
788 ///
789 /// If [trace] is passed, it's used as the stack trace for the error.
790 Future completerErrorFuture([StackTrace trace]) {
791 var completer = new Completer();
792 completer.completeError('error', trace);
793 return completer.future;
794 }
795
796 /// Returns a Stream that emits an error using a controller.
797 ///
798 /// If [trace] is passed, it's used as the stack trace for the error.
799 Stream controllerErrorStream([StackTrace trace]) {
800 var controller = new StreamController();
801 controller.addError('error', trace);
802 return controller.stream;
803 }
804
805 /// Runs [callback] within [asyncFn], then converts any errors raised into a
806 /// [Chain] with [Chain.forTrace].
807 Future<Chain> chainForTrace(asyncFn(callback()), callback()) {
808 var completer = new Completer();
809 asyncFn(() {
810 // We use `new Future.value().then(...)` here as opposed to [new Future] or
811 // [new Future.sync] because those methods don't pass the exception through
812 // the zone specification before propagating it, so there's no chance to
813 // attach a chain to its stack trace. See issue 15105.
814 new Future.value().then((_) => callback())
815 .catchError(completer.completeError);
816 });
817 return completer.future
818 .catchError((_, stackTrace) => new Chain.forTrace(stackTrace));
819 }
820
821 /// Runs [callback] in a [Chain.capture] zone and returns a Future that
822 /// completes to the stack chain for an error thrown by [callback].
823 ///
824 /// [callback] is expected to throw the string `"error"`.
825 Future<Chain> captureFuture(callback()) {
826 var completer = new Completer<Chain>();
827 Chain.capture(callback, onError: (error, chain) {
828 expect(error, equals('error'));
829 completer.complete(chain);
830 });
831 return completer.future;
832 }
OLDNEW
« no previous file with comments | « test/chain/utils.dart ('k') | test/chain_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698