// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. import 'dart:async'; import 'package:async/async.dart'; import 'package:term_glyph/term_glyph.dart' as glyph; import 'package:test/test.dart'; import 'utils_new.dart'; void main() { setUpAll(() { glyph.ascii = true; }); late Stream stream; late StreamQueue queue; late Stream errorStream; late StreamQueue errorQueue; setUp(() { stream = Stream.fromIterable([1, 2, 3, 4, 5]); queue = StreamQueue(Stream.fromIterable([1, 2, 3, 4, 5])); errorStream = Stream.fromFuture(Future.error('oh no!', StackTrace.current)); errorQueue = StreamQueue( Stream.fromFuture(Future.error('oh no!', StackTrace.current)), ); }); group('emits()', () { test('matches the first event of a Stream', () { expect(stream, emits(1)); }); test('rejects the first event of a Stream', () { expect( expectLater(stream, emits(2)), throwsTestFailure( allOf([ startsWith('Expected: should emit an event that <2>\n'), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n', ), ]), ), ); }); test('matches and consumes the next event of a StreamQueue', () { expect(queue, emits(1)); expect(queue.next, completion(equals(2))); expect(queue, emits(3)); expect(queue.next, completion(equals(4))); }); test('rejects and does not consume the first event of a StreamQueue', () { expect( expectLater(queue, emits(2)), throwsTestFailure( allOf([ startsWith('Expected: should emit an event that <2>\n'), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n', ), ]), ), ); expect(queue, emits(1)); }); test('rejects an empty stream', () { expect( expectLater(const Stream.empty(), emits(1)), throwsTestFailure( allOf([ startsWith('Expected: should emit an event that <1>\n'), endsWith(' Which: emitted x Stream closed.\n'), ]), ), ); }); test('forwards a stream error', () { expect(expectLater(errorStream, emits(1)), throwsA('oh no!')); }); test('wraps a normal matcher', () { expect(queue, emits(lessThan(5))); expect( expectLater(queue, emits(greaterThan(5))), throwsTestFailure(anything), ); }); test('returns a StreamMatcher as-is', () { expect(queue, emits(emitsThrough(4))); expect(queue, emits(5)); }); }); group('emitsDone', () { test('succeeds for an empty stream', () { expect(const Stream.empty(), emitsDone); }); test('fails for a stream with events', () { expect( expectLater(stream, emitsDone), throwsTestFailure( allOf([ startsWith('Expected: should be done\n'), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n', ), ]), ), ); }); }); group('emitsError()', () { test('consumes a matching error', () { expect(errorQueue, emitsError('oh no!')); expect(errorQueue.hasNext, completion(isFalse)); }); test('fails for a non-matching error', () { expect( expectLater(errorStream, emitsError('oh heck')), throwsTestFailure( allOf([ startsWith("Expected: should emit an error that 'oh heck'\n"), contains(' Which: emitted ! oh no!\n'), contains( ' x Stream closed.\n' " which threw 'oh no!'\n" ' stack ', ), endsWith( ' which is different.\n' ' Expected: oh heck\n' ' Actual: oh no!\n' ' ^\n' ' Differ at offset 3\n', ), ]), ), ); }); test('fails for a stream with events', () { expect( expectLater(stream, emitsDone), throwsTestFailure( allOf([ startsWith('Expected: should be done\n'), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n', ), ]), ), ); }); }); group('mayEmit()', () { test('consumes a matching event', () { expect(queue, mayEmit(1)); expect(queue, emits(2)); }); test('allows a non-matching event', () { expect(queue, mayEmit('fish')); expect(queue, emits(1)); }); }); group('emitsAnyOf()', () { test('consumes an event that matches a matcher', () { expect(queue, emitsAnyOf([2, 1, 3])); expect(queue, emits(2)); }); test('consumes as many events as possible', () { expect( queue, emitsAnyOf([ 1, emitsInOrder([1, 2]), emitsInOrder([1, 2, 3]), ]), ); expect(queue, emits(4)); }); test('fails if no matchers match', () { expect( expectLater(stream, emitsAnyOf([2, 3, 4])), throwsTestFailure( allOf([ startsWith( 'Expected: should do one of the following:\n' ' * emit an event that <2>\n' ' * emit an event that <3>\n' ' * emit an event that <4>\n', ), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n' ' which failed all options:\n' ' * failed to emit an event that <2>\n' ' * failed to emit an event that <3>\n' ' * failed to emit an event that <4>\n', ), ]), ), ); }); test('allows an error if any matcher matches', () { expect(errorStream, emitsAnyOf([1, 2, emitsError('oh no!')])); }); test('rethrows an error if no matcher matches', () { expect( expectLater(errorStream, emitsAnyOf([1, 2, 3])), throwsA('oh no!'), ); }); }); group('emitsInOrder()', () { test('consumes matching events', () { expect(queue, emitsInOrder([1, 2, emitsThrough(4)])); expect(queue, emits(5)); }); test("fails if the matchers don't match in order", () { expect( expectLater(queue, emitsInOrder([1, 3, 2])), throwsTestFailure( allOf([ startsWith( 'Expected: should do the following in order:\n' ' * emit an event that <1>\n' ' * emit an event that <3>\n' ' * emit an event that <2>\n', ), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n' " which didn't emit an event that <3>\n", ), ]), ), ); }); }); group('emitsThrough()', () { test('consumes events including those matching the matcher', () { expect(queue, emitsThrough(emitsInOrder([3, 4]))); expect(queue, emits(5)); }); test('consumes the entire queue with emitsDone', () { expect(queue, emitsThrough(emitsDone)); expect(queue.hasNext, completion(isFalse)); }); test('fails if the queue never matches the matcher', () { expect( expectLater(queue, emitsThrough(6)), throwsTestFailure( allOf([ startsWith('Expected: should eventually emit an event that <6>\n'), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n' ' which never did emit an event that <6>\n', ), ]), ), ); }); }); group('mayEmitMultiple()', () { test('consumes multiple instances of the given matcher', () { expect(queue, mayEmitMultiple(lessThan(3))); expect(queue, emits(3)); }); test('consumes zero instances of the given matcher', () { expect(queue, mayEmitMultiple(6)); expect(queue, emits(1)); }); test("doesn't rethrow errors", () { expect(errorQueue, mayEmitMultiple(1)); expect(errorQueue, emitsError('oh no!')); }); }); group('neverEmits()', () { test('succeeds if the event never matches', () { expect(queue, neverEmits(6)); expect(queue, emits(1)); }); test('fails if the event matches', () { expect( expectLater(stream, neverEmits(4)), throwsTestFailure( allOf([ startsWith('Expected: should never emit an event that <4>\n'), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n' ' which after 3 events did emit an event that <4>\n', ), ]), ), ); }); test('fails if emitsDone matches', () { expect( expectLater(stream, neverEmits(emitsDone)), throwsTestFailure(anything), ); }); test("doesn't rethrow errors", () { expect(errorQueue, neverEmits(6)); expect(errorQueue, emitsError('oh no!')); }); }); group('emitsInAnyOrder()', () { test('consumes events that match in any order', () { expect(queue, emitsInAnyOrder([3, 1, 2])); expect(queue, emits(4)); }); test("fails if the events don't match in any order", () { expect( expectLater(stream, emitsInAnyOrder([4, 1, 2])), throwsTestFailure( allOf([ startsWith( 'Expected: should do the following in any order:\n' ' * emit an event that <4>\n' ' * emit an event that <1>\n' ' * emit an event that <2>\n', ), endsWith( ' Which: emitted * 1\n' ' * 2\n' ' * 3\n' ' * 4\n' ' * 5\n' ' x Stream closed.\n', ), ]), ), ); }); test("doesn't rethrow if some ordering matches", () { expect(errorQueue, emitsInAnyOrder([emitsDone, emitsError('oh no!')])); }); test('rethrows if no ordering matches', () { expect( expectLater(errorQueue, emitsInAnyOrder([1, emitsError('oh no!')])), throwsA('oh no!'), ); }); }); test('A custom StreamController doesn\'t hang on close', () async { var controller = StreamController(); var done = expectLater(controller.stream, emits(null)); controller.add(null); await done; await controller.close(); }); }